Spark結構化流編程【Dataset、DataFrame】

一.DataFrame和DataSet的API

從Spark 2.0開始,DataFrame和Dataset可以表示靜態的有界數據以及流式無界數據。與靜態數據集/數據框類似,可以使用公共入口點SparkSession (Scala / Java / Python / R docs)從流源創建流式數據框/數據集,並對它們應用與靜態數據框/數據集相同的操作。

二.創建DataFrame和DataSet

可以通過由返回的DataStreamReader接口(Scala / Java / Python文檔)的SparkSession.readStream()創建流式DataFrame 。在R中,使用read.stream()方法。與用於創建靜態DataFrame的讀取接口類似,可以指定源的詳細信息:數據格式,架構,選項等。
1.輸入源【內置】

  • File Source文件源:讀取寫入目錄的文件作爲數據流。支持的文件格式爲text,csv,json,orc,parquet。請注意,文件必須原子地放置在給定目錄中,在大多數文件系統中,這可以通過文件移動操作來實現。
  • Kafka:從Kafka讀取數據。它與0.10.0或更高版本的Kafka代理兼容。
  • Socket【用於測試】:從套接字連接讀取UTF8文本數據。監聽服務器套接字位於驅動程序處。請注意,這僅應用於測試,因爲這不能提供端到端的容錯保證。
  • Rate source【用於測試】:以每秒指定的行數生成數據,每個輸出行包含timestamp和value。where timestamp是Timestamp包含消息分發時間的類型,並且value是Long包含消息計數的類型,從0開始作爲第一行。此源旨在進行測試和基準測試。

某些源不是容錯的,因爲它們不能保證故障後可以使用檢查點偏移來重放數據。
在這裏插入圖片描述
在這裏插入圖片描述
2.例子

val spark: SparkSession = ...

// Read text from socket
val socketDF = spark
  .readStream
  .format("socket")
  .option("host", "localhost")
  .option("port", 9999)
  .load()

socketDF.isStreaming    // Returns True for DataFrames that have streaming sources

socketDF.printSchema

// Read all the csv files written atomically in a directory
val userSchema = new StructType().add("name", "string").add("age", "integer")
val csvDF = spark
  .readStream
  .option("sep", ";")
  .schema(userSchema)      // Specify schema of the csv files
  .csv("/path/to/directory")    // Equivalent to format("csv").load("/path/to/directory")

這些示例生成未類型化的流式DataFrame,這意味着在編譯時不檢查DataFrame的架構,僅在提交查詢時在運行時檢查它。有些操作(如map,flatMap等)需要在編譯時知道類型。爲此,可以使用與靜態DataFrame相同的方法將這些未類型化的流數據框架轉換爲已類型化的流數據集。

三.streaming DataFrame/DataSet的模式推斷和分區

默認情況下,從基於文件的源進行結構化流傳輸需要指定架構,而不是依靠Spark自動推斷。此限制確保即使在發生故障的情況下,也將一致的架構用於流查詢。對於臨時用例,可以通過將設置spark.sql.streaming.schemaInference爲true來重新啓用模式推斷。

當存在命名的子目錄時,分區發現確實會發生,/key=value/並且列表將自動遞歸到這些目錄中。如果這些列出現在用戶提供的架構中,Spark將根據讀取文件的路徑來填充它們。啓動查詢時,必須存在組成分區方案的目錄,並且這些目錄必須保持靜態。

四.基本操作-選擇,投影,彙總

流上支持DataFrame / Dataset上的大多數常見操作。稍後將討論不支持的一些操作。

case class DeviceData(device: String, deviceType: String, signal: Double, time: DateTime)

val df: DataFrame = ... // streaming DataFrame with IOT device data with schema { device: string, deviceType: string, signal: double, time: string }
val ds: Dataset[DeviceData] = df.as[DeviceData]    // streaming Dataset with IOT device data

// Select the devices which have signal more than 10
df.select("device").where("signal > 10")      // using untyped APIs   
ds.filter(_.signal > 10).map(_.device)         // using typed APIs

// Running count of the number of updates for each device type
df.groupBy("deviceType").count()                          // using untyped API

// Running average signal for each device type
import org.apache.spark.sql.expressions.scalalang.typed
ds.groupByKey(_.deviceType).agg(typed.avg(_.signal))    // using typed API

還可以將流式DataFrame / Dataset註冊爲臨時視圖,然後在其上應用SQL命令。

df.createOrReplaceTempView("updates")
spark.sql("select count(*) from updates")  // returns another streaming DF

注意,可以使用df.isStreaming來確定DataFrame / Dataset是否具有流數據。

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