Spark最吸引開發者的就是簡單易用、跨語言(Scala, Java, Python, and R)的API。
本文主要講解Apache Spark 2.0中RDD,DataFrame和Dataset三種API;它們各自適合的使用場景;它們的性能和優化;列舉使用DataFrame和DataSet代替RDD的場景。本文聚焦DataFrame和Dataset,因為這是Apache Spark 2.0的API統一的重點。
Apache Spark 2.0統一API的主要動機是:簡化Spark。通過減少用戶學習的概念和提供結構化的數據進行處理。除了結構化,Spark也提供higher-level抽象和API作為特定領域語言(DSL)。
彈性數據集(RDD)
RDD是Spark建立之初的核心API。RDD是不可變分布式彈性數據集,在Spark集群中可跨節點分區,並提供分布式low-level API來操作RDD,包括transformation和action。
何時使用RDD?
使用RDD的一般場景:
你需要使用low-level的transformation和action來控制你的數據集;
你的數據集非結構化,比如:流媒體或者文本流;
你想使用函數式編程來操作你的數據,而不是用特定領域語言(DSL)表達;
你不想加入schema,比如,當通過名字或者列處理(或訪問)數據屬性不在意列式存儲格式;
當你可以放棄使用DataFrame和Dataset來優化結構化和半結構化數據集的時候。
在Spark2.0中RDD發生了什么
你可能會問:RDD是不是成為“二等公民”了?或者是不是干脆以后不用了?
答案當然是NO!
通過后面的描述你會得知:Spark用戶可以在RDD,DataFrame和Dataset三種數據集之間無縫轉換,而且只需要使用超級簡單的API方法。
DataFrames
DataFrame與RDD相同之處,都是不可變分布式彈性數據集。不同之處在於,DataFrame的數據集都是按指定列存儲,即結構化數據。類似於傳統數據庫中的表。DataFrame的設計是為了讓大數據處理起來更容易。DataFrame允許開發者把結構化數據集導入DataFrame,並做了higher-level的抽象;DataFrame提供特定領域的語言(DSL)API來操作你的數據集。
在Spark2.0中,DataFrame API將會和Dataset API合並,統一數據處理API。由於這個統一“有點急”,導致大部分Spark開發者對Dataset的high-level和type-safe API並沒有什么概念。

DataSets
從Spark2.0開始,DataSets扮演了兩種不同的角色:強類型API和弱類型API,見下表。從概念上來講,可以把DataFrame 當作一個泛型對象的集合DataSet[Row], Row是一個弱類型JVM 對象。相對應地,如果JVM對象是通過Scala的case class或者Java class來表示的,Dataset是強類型的。

Dataset API的優勢
對於Spark開發者而言,你將從Spark 2.0的DataFrame和Dataset統一的API獲得以下好處:
1. 靜態類型和運行時類型安全
考慮靜態類型和運行時類型安全,SQL有很少的限制而Dataset限制很多。例如,Spark SQL查詢語句,你直到運行時才能發現語法錯誤(syntax error),代價較大。然后DataFrame和Dataset在編譯時就可捕捉到錯誤,節約開發時間和成本。
Dataset API都是lambda函數和JVM typed object,任何typed-parameters不匹配即會在編譯階段報錯。因此使用Dataset節約開發時間。

2. High-level抽象以及結構化和半結構化數據集的自定義視圖
DataFrame是Dataset[Row]的集合,把結構化數據集視圖轉換成半結構化數據集。例如,有個海量IoT設備事件數據集,用JSON格式表示。JSON是一個半結構化數據格式,很適合使用Dataset, 轉成強類型的Dataset[DeviceIoTData]。
使用Scala為JSON數據DeviceIoTData定義case class。

緊接着,從JSON文件讀取數據

上面代碼運行時底層會發生下面3件事。
Spark讀取JSON文件,推斷出其schema,創建一個DataFrame;
Spark把數據集轉換DataFrame -> Dataset[Row],泛型Row object,因為這時還不知道其確切類型;
Spark進行轉換:Dataset[Row] -> Dataset[DeviceIoTData],DeviceIoTData類的Scala JVM object
我們的大多數人,在操作結構化數據時,都習慣於以列的方式查看和處理數據列,或者訪問對象的指定列。Dataset 是Dataset[ElementType]類型對象的集合,既可以編譯時類型安全,也可以為強類型的JVM對象定義視圖。從上面代碼獲取到的數據可以很簡單的展示出來,或者用高層方法處理。

3. 簡單易用的API
雖然結構化數據會給Spark程序操作數據集帶來挺多限制,但它卻引進了豐富的語義和易用的特定領域語言。大部分計算可以被Dataset的high-level API所支持。例如,簡單的操作agg,select,avg,map,filter或者groupBy即可訪問DeviceIoTData類型的Dataset。
使用特定領域語言API進行計算是非常簡單的。例如,使用filter()和map()創建另一個Dataset。
把計算過程翻譯成領域API比RDD的關系代數式表達式要容易的多。例如:

4. 性能和優化
使用DataFrame和Dataset API獲得空間效率和性能優化的兩個原因:
首先:因為DataFrame和Dataset是在Spark SQL 引擎上構建的,它會使用Catalyst優化器來生成優化過的邏輯計划和物理查詢計划。
R,Java,Scala或者Python的DataFrame/Dataset API,所有的關系型的查詢都運行在相同的代碼優化器下,代碼優化器帶來的的是空間和速度的提升。不同的是Dataset[T]強類型API優化數據引擎任務,而弱類型API DataFrame在交互式分析場景上更快,更合適。

其次,通過博客https://databricks.com/blog/2016/05/23/apache-spark-as-a-compiler-joining-a-billion-rows-per-second-on-a-laptop.html 可以知道:Dataset能使用Encoder映射特定類型的JVM 對象到Tungsten內部內存表示。Tungsten的Encoder可以有效的序列化/反序列化JVM object,生成字節碼來提高執行速度。
什么時候使用DataFrame或者Dataset?
你想使用豐富的語義,high-level抽象,和特定領域語言API,那你可以使用DataFrame或者Dataset;
你處理的半結構化數據集需要high-level表達,filter,map,aggregation,average,sum,SQL查詢,列式訪問和使用lambda函數,那你可以使用DataFrame或者Dataset;
想利用編譯時高度的type-safety,Catalyst優化和Tungsten的code生成,那你可以使用DataFrame或者Dataset;
你想統一和簡化API使用跨Spark的Library,那你可以使用DataFrame或者Dataset;
如果你是一個R使用者,那你可以使用DataFrame或者Dataset;
如果你是一個Python使用者,那你可以使用DataFrame或者Dataset。
你可以無縫地把DataFrame或者Dataset轉化成一個RDD,只需簡單的調用.rdd:
總結
通過上面的分析,什么情況選擇RDD,DataFrame還是Dataset已經很明顯了。RDD適合需要low-level函數式編程和操作數據集的情況;DataFrame和Dataset適合結構化數據集,使用high-level和特定領域語言(DSL)編程,空間效率高和速度快。