Spark學習08——RDD、DataFrame 和 Dataset

彈性分佈式數據集(Resilient Distributed Dataset,RDD)

從一開始 RDD 就是 Spark 提供的面向用戶的主要 API。從根本上來說,一個 RDD 就是你的數據的一個不可變的分佈式元素集合,在集羣中跨節點分佈,可以通過若干提供了轉換和處理的底層 API 進行並行處理。

在什麼情況下使用 RDD?
下面是使用 RDD 的場景和常見案例:

你希望可以對你的數據集進行最基本的轉換、處理和控制;
你的數據是非結構化的,比如流媒體或者字符流;
你想通過函數式編程而不是特定領域內的表達來處理你的數據;
你不希望像進行列式處理一樣定義一個模式,通過名字或字段來處理或訪問數據屬性;
你並不在意通過 DataFrame 和 Dataset 進行結構化和半結構化數據處理所能獲得的一些優化和性能上的好處;

Dataset

從 Spark 2.0 開始,Dataset 開始具有兩種不同類型的 API 特徵:有明確類型的 API 和無類型的 API。從概念上來說,你可以把 DataFrame 當作一些通用對象 Dataset[Row] 的集合的一個別名,而一行就是一個通用的無類型的 JVM 對象。與之形成對比,Dataset 就是一些有明確類型定義的 JVM 對象的集合,通過你在 Scala 中定義的 Case Class 或者 Java 中的 Class 來指定。

DataFrame

DataFrame 當作一些通用對象 Dataset[Row] 的集合的一個別名,而一行就是一個通用的無類型的 JVM 對象
有類型和無類型的 API
語言

主要抽象

Scala Dataset[T] & DataFrame (Dataset[Row] 的別名)

Java   Dataset[T]

Python DataFrame

R    DataFrame

注意:因爲 Python 和 R 沒有編譯時類型安全,所以我們只有稱之爲 DataFrame 的無類型 API。

該什麼時候使用 DataFrame 或 Dataset 呢?

如果你需要豐富的語義、高級抽象和特定領域專用的 API,那就使用 DataFrame 或 Dataset;
如果你的處理需要對半結構化數據進行高級處理,如 filter、map、aggregation、average、sum、SQL 查詢、列式訪問或使用 lambda 函數,那就使用 DataFrame 或 Dataset;
如果你想在編譯時就有高度的類型安全,想要有類型的 JVM 對象,用上 Catalyst 優化,並得益於 Tungsten 生成的高效代碼,那就使用 Dataset;
如果你想在不同的 Spark 庫之間使用一致和簡化的 API,那就使用 DataFrame 或 Dataset;
如果你是 R 語言使用者,就用 DataFrame;
如果你是 Python 語言使用者,就用 DataFrame,在需要更細緻的控制時就退回去使用 RDD;
注意只需要簡單地調用一下.rdd,就可以無縫地將 DataFrame 或 Dataset 轉換成 RDD

Dataset API 的優點

1、靜態類型與運行時類型安全
從 SQL 的最小約束到 Dataset 的最嚴格約束,把靜態類型和運行時安全想像成一個圖譜。比如,如果你用的是 Spark SQL 的查詢語句,要直到運行時你纔會發現有語法錯誤(這樣做代價很大),而如果你用的是 DataFrame 和 Dataset,你在編譯時就可以捕獲錯誤(這樣就節省了開發者的時間和整體代價)。也就是說,當你在 DataFrame 中調用了 API 之外的函數時,編譯器就可以發現這個錯。不過,如果你使用了一個不存在的字段名字,那就要到運行時才能發現錯誤了。

圖譜的另一端是最嚴格的 Dataset。因爲 Dataset API 都是用 lambda 函數和 JVM 類型對象表示的,所有不匹配的類型參數都可以在編譯時發現。而且在使用 Dataset 時,你的分析錯誤也會在編譯時被發現,這樣就節省了開發者的時間和代價。

所有這些最終都被解釋成關於類型安全的圖譜,內容就是你的 Spark 代碼裏的語法和分析錯誤。在圖譜中,Dataset 是最嚴格的一端,但對於開發者來說也是效率最高的。

在這裏插入圖片描述

2、關於結構化和半結構化數據的高級抽象和定製視圖
把 DataFrame 當成 Dataset[Row] 的集合,就可以對你的半結構化數據有了一個結構化的定製視圖。比如,假如你有個非常大量的用 JSON 格式表示的物聯網設備事件數據集。因爲 JSON 是半結構化的格式,那它就非常適合採用 Dataset 來作爲強類型化的 Dataset[DeviceIoTData] 的集合。

{"device_id": 198164, "device_name": "sensor-pad-198164owomcJZ", "ip": "80.55.20.25", "cca2": "PL", "cca3": "POL", "cn": "Poland", "latitude": 53.080000, "longitude": 18.620000, "scale": "Celsius", "temp": 21, "humidity": 65, "battery_level": 8, "c02_level": 1408, "lcd": "red", "timestamp" :1458081226051}

你可以用一個 Scala Case Class 來把每條 JSON 記錄都表示爲一條 DeviceIoTData,一個定製化的對象。

case class DeviceIoTData (battery_level: Long, c02_level: Long, cca2: 
String, cca3: String, cn: String, device_id: Long, device_name: String, humidity: 
Long, ip: String, latitude: Double, lcd: String, longitude: Double, scale:String, temp: Long, timestamp: Long)

接下來,我們就可以從一個 JSON 文件中讀入數據。

// read the json file and create the dataset from the 
// case class DeviceIoTData
// ds is now a collection of JVM Scala objects DeviceIoTData
val ds = spark.read.json(“/databricks-public-datasets/data/iot/iot_devices.json”).as[DeviceIoTData]

上面的代碼其實可以細分爲三步:

1.Spark 讀入 JSON,根據模式創建出一個 DataFrame 的集合;
2.在這時候,Spark 把你的數據用“DataFrame = Dataset[Row]”進行轉換,變成一種通用行對象的集合,因爲這時候它還不知道具體的類型;
3.然後,Spark 就可以按照類 DeviceIoTData 的定義,轉換出“Dataset[Row] -> Dataset[DeviceIoTData]”這樣特定類型的 Scala JVM 對象了。

許多和結構化數據打過交道的人都習慣於用列的模式查看和處理數據,或者訪問對象中的某個特定屬性。將 Dataset 作爲一個有類型的 Dataset[ElementType] 對象的集合,你就可以非常自然地又得到編譯時安全的特性,又爲強類型的 JVM 對象獲得定製的視圖。而且你用上面的代碼獲得的強類型的 Dataset[T] 也可以非常容易地用高級方法展示或處理。

在這裏插入圖片描述
3、方便易用的結構化 API
雖然結構化可能會限制你的 Spark 程序對數據的控制,但它卻提供了豐富的語義,和方便易用的特定領域內的操作,後者可以被表示爲高級結構。事實上,用 Dataset 的高級 API 可以完成大多數的計算。比如,它比用 RDD 數據行的數據字段進行 agg、select、sum、avg、map、filter 或 groupBy 等操作簡單得多,只需要處理 Dataset 類型的 DeviceIoTData 對象即可。

用一套特定領域內的 API 來表達你的算法,比用 RDD 來進行關係代數運算簡單得多。比如,下面的代碼將用 filter() 和 map() 來創建另一個不可變 Dataset。

// Use filter(), map(), groupBy() country, and compute avg() 
// for temperatures and humidity. This operation results in 
// another immutable Dataset. The query is simpler to read, 
// and expressive
val dsAvgTmp = ds.filter(d => {d.temp > 25}).map(d => (d.temp, d.humidity, d.cca3)).groupBy($"_3").avg()
//display the resulting dataset
display(dsAvgTmp)

在這裏插入圖片描述

4、性能與優化
除了上述優點之外,你還要看到使用 DataFrame 和 Dataset API 帶來的空間效率和性能提升。原因有如下兩點:

首先,因爲 DataFrame 和 Dataset API 都是基於 Spark SQL 引擎構建的,它使用 Catalyst 來生成優化後的邏輯和物理查詢計劃。所有 R、Java、Scala 或 Python 的 DataFrame/Dataset API,所有的關係型查詢的底層使用的都是相同的代碼優化器,因而會獲得空間和速度上的效率。儘管有類型的 Dataset[T] API 是對數據處理任務優化過的,無類型的 Dataset[Row](別名 DataFrame)卻運行得更快,適合交互式分析。

在這裏插入圖片描述

總結

總之,在什麼時候該選用 RDD、DataFrame 或 Dataset 看起來好像挺明顯。前者可以提供底層的功能和控制,後者支持定製的視圖和結構,可以提供高級和特定領域的操作,節約空間並快速運行。

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