SparkSQL 實現UV & PV計算

背景

前兩天面試中遇到一個比較基礎的計算UV & PV 的問題。思路比較簡單,最重要的是 手寫代碼 ,平常我們都是在IDE 中編寫代碼,手寫代碼的時候大多是情況下都是使用IDE 的提示,遇到手寫的時候,就算這種簡單的代碼也不一定寫得出來。

那天採取的一個思路是:先把思路寫出來,然後,時間夠再添代碼進去。有時候確實一些函數拼不出來什麼的,但是思路在,好過白卷。

由上面的背景引出 使用SparkSQL 實現 統計 UV & PV 的問題。

數據如下:格式 ip,請求方式,路徑

192.168.0.112,post,/app2/index.html
192.168.2.11,get,/app1/user?id=3
192.168.2.11,post,/app1/submittoder
192.168.0.122,post,/app1/goods
....

需求: 求出每個APP 的訪問訪問次數(UV)和獨立IP 訪問次數(PV)

整個過程大概分爲4步:

1、先構建SparkSession 入口

//構建 sparksession
val sparkSession: SparkSession = SparkSession.builder().appName("pv_uv").master("local[3]").getOrCreate()

2、讀取文件

val lines: RDD[String] = sparkSession.sparkContext.textFile("C:\\Users\\Administrator\\Desktop\\testData\\log.txt")

3、整理數據並轉換爲 DataFrame

第一方式:導入隱式轉換,使用 toDF 函數把 RDD 轉換成 DataFrame

import sparkSession.implicits._
val ipAndAppDF: DataFrame = lines.map(line => {
  //切分一行數據
  val fileds: Array[String] = line.split(",")
  //提取 ip
  val ip: String = fileds(0)
  //應用
  val app: String = fileds(2).split("/")(1)
  (ip, app)
}).toDF("ip", "app")

第二種方式:使用 RDD[Row] + schema , 然後轉換爲 DataFrame

val ipAndAppRDD: RDD[Row] = lines.map(line => {
  //切分一行數據
  val fileds: Array[String] = line.split(",")
  //提取 ip
  val ip: String = fileds(0)
  //應用
  val app: String = fileds(2).split("/")(1)
  // 包裝成 Row
  Row(ip, app)
})
//準備schema 信息
val schema = StructType(List(
  StructField("ip", StringType, true),
  StructField("app", StringType, true)
))
//將RowRDD 關聯 schema
val bdf: DataFrame = sparkSession.createDataFrame(ipAndAppRDD, schema)

4、計算 PV & UV

第一種方式: 使用 SQL 方式,需要先進行註冊臨時表信息

//執行 sql 計算 uv & pv
val pv_uv: DataFrame = sparkSession.sql(" select app,count(1) as pv,count(distinct ip) as uv from  v_log group by app ")
// 打印數據
pv_uv.show()

第二種方式: 使用調用內置函數的方式計算 pv & uv,需要導入函數

// 導入spark.sql 內置函數 ,去重方法 countDistinct 注意使用方法
import org.apache.spark.sql.functions._
val result: DataFrame = ipAndAppDF.groupBy($"app").agg(count("*") as "pv", countDistinct('ip) as "uv")
// 打印數據
result.show()

5、關閉資源

sparkSession.stop()

最終結果:
在這裏插入圖片描述

建議:平常在寫代碼的時候,儘量能自己把 api 敲全,能不使用提示最好,這樣會更能加深印象。遇到變態的手寫代碼面試題,可能會有幫助

最後上完整代碼

package com.zhouq.spark.sql

import org.apache.spark.rdd.RDD
import org.apache.spark.sql.types._
import org.apache.spark.sql.{DataFrame, Row, SparkSession}

/**
  * 面試題 使用sparksql 查UV PV
  *
  * 數據格式:ip,請求方式,url
  *
  * 192.168.0.112,post,/app2/index.html
  *
  */
object LogUV_PV {

  def main(args: Array[String]): Unit = {

    /**
      * 第一步:創建 SparkSession
      */
    //構建 sparksession
    val sparkSession: SparkSession = SparkSession.builder().appName("pv_uv").master("local[3]").getOrCreate()

    /**
      * 第二步:讀取文件
      */
    val lines: RDD[String] = sparkSession.sparkContext.textFile("C:\\Users\\Administrator\\Desktop\\testData\\log.txt")

    /**
      * 第三步:整理數據並轉換爲 DataFrame
      */
    // 第一種方式:使用toDF 函數,需要導入隱式轉換,
    import sparkSession.implicits._

    val ipAndAppDF: DataFrame = lines.map(line => {
      //切分一行數據
      val fileds: Array[String] = line.split(",")
      //提取 ip
      val ip: String = fileds(0)
      //應用
      val app: String = fileds(2).split("/")(1)
      (ip, app)
    }).toDF("ip", "app")

    //第二種方式:使用RDD[Row] + schema ,然後轉換爲 DataFrame
//    val ipAndAppRDD: RDD[Row] = lines.map(line => {
//      //切分一行數據
//      val fileds: Array[String] = line.split(",")
//      //提取 ip
//      val ip: String = fileds(0)
//      //應用
//      val app: String = fileds(2).split("/")(1)
//      Row(ip, app)
//    })
//
//    //準備schema 信息
//    val schema = StructType(List(
//      StructField("ip", StringType, true),
//      StructField("app", StringType, true)
//    ))
//
//    //將RowRDD 關聯 schema
//    val bdf: DataFrame = sparkSession.createDataFrame(ipAndAppRDD, schema)

    /**
      * 第四步: 使用執行 SQL  的方式 計算 pv & uv
      */

    //註冊臨時表
    ipAndAppDF.createTempView("v_log")

    //執行 sql 計算 uv & pv
    val pv_uv: DataFrame = sparkSession.sql(" select app,count(1) as pv,count(distinct ip) as uv from  v_log group by app ")
    pv_uv.show()

//    /**
//      * 第四步: 使用調用內置函數的方式計算 pv & uv
//      * 需要導入 函數
//      */
//    import org.apache.spark.sql.functions._
//    val result: DataFrame = ipAndAppDF.groupBy($"app").agg(count("*") as "pv", countDistinct('ip) as "uv")
//
//    result.show()

    /**
      * 第五步:關閉資源
      */
    sparkSession.stop()
  }

}

有興趣的歡迎關注,大家一起交流學習。
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章