1. 前言
RDD、DataFrame、Dataset是Spark三個最重要的概念整份,RDD和DataFrame兩個概念出現(xiàn)的比較早待错,Dataset相對出現(xiàn)的較晚(1.6版本開始出現(xiàn)),有些開發(fā)人員對此還不熟悉烈评,本文重點引領快速理解Dataset火俄。
帶著幾個問題去閱讀:
1、DataFrame比RDD有哪些優(yōu)點讲冠?
2瓜客、DataFrame和Dataset有什么關系?
3竿开、有了DataFrame為什么還有引入Dataset谱仪?
4、Dataset在Spark源碼中長什么樣否彩?
注:本文的環(huán)境基于當前最新版本 Spark-2.1.1
2. RDD/DataFrame快速回顧
RDD
彈性分布式數(shù)據(jù)集疯攒,是Spark對數(shù)據(jù)進行的一種抽象,可以理解為Spark對數(shù)據(jù)的一種組織方式列荔,更簡單些說敬尺,RDD就是一種數(shù)據(jù)結(jié)構,里面包含了數(shù)據(jù)和操作數(shù)據(jù)的方法
從字面上就能看出的幾個特點:
彈性:
- 數(shù)據(jù)可完全放內(nèi)存或完全放磁盤贴浙,也可部分存放在內(nèi)存砂吞,部分存放在磁盤,并可以自動切換
- RDD出錯后可自動重新計算(通過血緣自動容錯)
- 可checkpoint(設置檢查點崎溃,用于容錯)蜻直,可persist或cache(緩存)
- 里面的數(shù)據(jù)是分片的(也叫分區(qū),partition)袁串,分片的大小可自由設置和細粒度調(diào)整
分布式:
- RDD中的數(shù)據(jù)可存放在多個節(jié)點上
數(shù)據(jù)集:
- 數(shù)據(jù)的集合概而,沒啥好說的
相對于與DataFrame和Dataset,RDD是Spark最底層的抽象般婆,目前是開發(fā)者用的最多的到腥,但逐步會轉(zhuǎn)向DataFrame和Dataset(當然,這是Spark的發(fā)展趨勢)
DataFrame
DataFrame:理解了RDD蔚袍,DataFrame就容易理解些,DataFrame的思想來源于Python的pandas庫配名,RDD是一個數(shù)據(jù)集啤咽,DataFrame在RDD的基礎上加了Schema(描述數(shù)據(jù)的信息,可以認為是元數(shù)據(jù)渠脉,DataFrame曾經(jīng)就有個名字叫SchemaRDD)
假設RDD中的兩行數(shù)據(jù)長這樣
那么DataFrame中的數(shù)據(jù)長這樣
從上面兩個圖可以看出宇整,DataFrame比RDD多了一個表頭信息(Schema),像一張表了芋膘,DataFrame還配套了新的操作數(shù)據(jù)的方法鳞青,DataFrame API(如df.select())和SQL(select id, name from xx_table where ...)霸饲。
有了DataFrame這個高一層的抽象后,我們處理數(shù)據(jù)更加簡單了臂拓,甚至可以用SQL來處理數(shù)據(jù)了厚脉,對開發(fā)者來說,易用性有了很大的提升胶惰。
不僅如此傻工,通過DataFrame API或SQL處理數(shù)據(jù),會自動經(jīng)過Spark 優(yōu)化器(Catalyst)的優(yōu)化孵滞,即使你寫的程序或SQL不高效中捆,也可以運行的很快,很爽吧坊饶!
注意:DataFrame是用來處理結(jié)構化數(shù)據(jù)的
3. 步入正文泄伪,Dataset
官方解釋如下(英語不好的同學隨意的瞄一眼即可):
A Dataset is a distributed collection of data. Dataset is a new interface added in Spark 1.6 that provides the benefits of RDDs (strong typing, ability to use powerful lambda functions) with the benefits of Spark SQL’s optimized execution engine. A Dataset can be constructed from JVM objects and then manipulated using functional transformations (map, flatMap, filter, etc.). The Dataset API is available in Scala and Java. Python does not have the support for the Dataset API. But due to Python’s dynamic nature, many of the benefits of the Dataset API are already available (i.e. you can access the field of a row by name naturally row.columnName). The case for R is similar.
相對于RDD,Dataset提供了強類型支持匿级,也是在RDD的每行數(shù)據(jù)加了類型約束
假設RDD中的兩行數(shù)據(jù)長這樣
那么Dataset中的數(shù)據(jù)長這樣
或者長這樣(每行數(shù)據(jù)是個Object)
使用Dataset API的程序臂容,會經(jīng)過Spark SQL的優(yōu)化器進行優(yōu)化(優(yōu)化器叫什么還記得嗎?)
目前僅支持Scala根蟹、Java API脓杉,尚未提供Python的API(所以一定要學習Scala)
相比DataFrame,Dataset提供了編譯時類型檢查简逮,對于分布式程序來講球散,提交一次作業(yè)太費勁了(要編譯、打包散庶、上傳蕉堰、運行),到提交到集群運行時才發(fā)現(xiàn)錯誤悲龟,實在是想罵人屋讶,這也是引入Dataset的一個重要原因。
使用DataFrame的代碼中json文件中并沒有score字段须教,但是能編譯通過皿渗,但是運行時會報異常!如下圖代碼所示
而使用Dataset實現(xiàn)轻腺,會在IDE中就報錯乐疆,出錯提前到了編譯之前
RDD轉(zhuǎn)換DataFrame后不可逆,但RDD轉(zhuǎn)換Dataset是可逆的(這也是Dataset產(chǎn)生的原因)贬养。如下操作所示:
-
啟動spark-shell挤土,創(chuàng)建一個RDD
通過RDD創(chuàng)建DataFrame,再通過DataFrame轉(zhuǎn)換成RDD误算,發(fā)現(xiàn)RDD的類型變成了Row類型
- 通過RDD創(chuàng)建Dataset仰美,再通過Dataset轉(zhuǎn)換為RDD迷殿,發(fā)現(xiàn)RDD還是原始類型
4. Dataset基本操作
將Spark安裝目錄的LICENSE文件上傳至HDFS上,將文件讀入Spark咖杂,使用as[]轉(zhuǎn)換為DataSet
使用Dataset API做轉(zhuǎn)換操作
創(chuàng)建臨時視圖庆寺,進行SQL操作
使用SQL進行單詞統(tǒng)計
使用SQL進行排名分析
5. Dataset源碼初探
如果不想本地搭建源碼閱讀環(huán)境,推薦一款在線閱讀源碼的工具insight翰苫,不需要本地環(huán)境止邮,可以直接引用github中的代碼,非常方便.
Dataset的源碼位于sql目錄下:
sql/core/src/main/scala/org/apache/spark/sql/Dataset.scala
由此可以看出Dataset是Spark SQL組件中的東西奏窑,另外DataFrame也是SparkSQL中的東西(這也是為什么Spark SQL是Spark生態(tài)中發(fā)展最迅猛的模塊)
上圖顯示了Dataset文件的結(jié)構导披,可以看出:
- Dataset是個類和一個伴生對象
- 里面包含了一些變量,比如我們常用的sqlContext
- 里面有很多函數(shù)和算子埃唯,比如toDF撩匕、map等操作數(shù)據(jù)的算子
前面我們說了,Dataset是個組織數(shù)據(jù)的的結(jié)構墨叛,那么數(shù)據(jù)存儲在哪里呢止毕?
- Dataset定義在sql這個包中
- 主構造函數(shù)中需要傳遞三個參數(shù)
sparkSession:運行環(huán)境信息
queryExecution:數(shù)據(jù)和執(zhí)行邏輯信息。注意漠趁,數(shù)據(jù)在這個參數(shù)中
encoder:編碼器扁凛,用于將JVM對象轉(zhuǎn)換為SparkSQL的對象(當然這里會有序列化和Schema等)
我們可以使用createDataset函數(shù)來創(chuàng)建一個Dataset,如上圖所示闯传。
調(diào)用這個函數(shù)時谨朝,究竟發(fā)生了什么呢?
我們來看這個函數(shù)的實現(xiàn):
- 將數(shù)據(jù)傳進來后甥绿,提取數(shù)據(jù)的類型字币,通過類型提取Schema,并將數(shù)據(jù)轉(zhuǎn)換成SparkSQL內(nèi)部的Row(InternalRow)
- 將數(shù)據(jù)和數(shù)據(jù)屬性信息封裝成一個relation對象
- 用Dataset的伴生對象創(chuàng)建Dataset實例共缕,這里的self就是SparkSession對象
- Dataset伴生對象中的apply方法來new Dataset生成對象洗出,如下圖所示
第三個參數(shù)沒傳,使用了隱式的encoder(createDataset中的encoded變量取名不規(guī)范图谷,容易混淆)
一張簡單的圖翩活,總結(jié)了創(chuàng)建Dataset兩個最重要的步驟
到此為止我們在源碼級別粗略的看了Dataset和創(chuàng)建流程,主要是幫助理解Dataset的概念蜓萄,更多技術細節(jié)不適合本文描述隅茎。