RDD(Resilient Distributed Datasets)彈性的分佈式數據集,又稱Spark core,它表明一個只讀的、不可變、可分區,裏面的元素可分佈式並行計算的數據集。sql
RDD是一個很抽象的概念,不易於理解,可是要想學好Spark,必需要掌握RDD,熟悉它的編程模型,這是學習Spark其餘組件的基礎。筆者在這裏從名字和幾個重要的概念給你們一一解讀:編程
Resilient(彈性的)緩存
提到大數據必提分佈式,而在大規模的分佈式集羣中,任何一臺服務器隨時都有可能出現故障,若是一個task任務所在的服務器出現故障,必然致使這個task執行失敗。此時,RDD的"彈性的"特色可使這個task在集羣內進行遷移,從而保證總體任務對故障服務器的平穩過渡。對於整個任務而言,只需重跑某些失敗的task便可,而無需徹底重跑,大大提升性能服務器
Distributed(分佈式)微信
首先了解一下分區,即數據根據必定的切分規則切分紅一個個的子集。spark中分區劃分規則默認是根據key進行哈希取模,切分後的數據子集能夠獨立運行在各個task中而且在各個集羣服務器中並行執行。固然使用者也能夠自定義分區規則,這個仍是頗有應用場景的,好比自定義分區打散某個key特別多的數據集以免數據傾斜(數據傾斜是大數據領域常見問題也是調優重點,後續會單獨講解)編程語言
Datasets(數據集)分佈式
初學者很容易誤解,認爲RDD是存儲數據的,畢竟從名字看來它是一個"彈性的分佈式數據集"。可是,筆者強調,RDD並不存儲數據,它只記錄數據存儲的位置。內部處理邏輯是經過使用者調用不一樣的Spark算子,一個RDD會轉換爲另外一個RDD(這也體現了RDD只讀不可變的特色,即一個RDD只能由另外一個RDD轉換而來),以transformation算子爲例,RDD彼此之間會造成pipeline管道,無需等到上一個RDD全部數據處理邏輯執行完就能夠當即交給下一個RDD進行處理,性能也獲得了很大提高。可是RDD在進行transform時,不是每處理一條數據就交給下一個RDD,而是使用小批量的方式進行傳遞(這也是一個優化點)函數式編程
lineage函數
既然Spark將RDD之間以pipeline的管道鏈接起來,如何避免在服務器出現故障後,重算這些數據呢?這些失敗的RDD由哪來呢?這就牽涉到,Spark中的一個很重要的概念:Lineage即血統關係。它會記錄RDD的元數據信息和依賴關係,當該RDD的部分分區數據丟失時,能夠根據這些信息來從新運算和恢復丟失的分區數據。簡單而言就是它會記錄哪些RDD是怎麼產生的、怎麼「丟失」的等,而後Spark會根據lineage記錄的信息,恢復丟失的數據子集,這也是保證Spark RDD彈性的關鍵點之一性能
Spark緩存和checkpoint
緩存(cache/persist)
cache和persist實際上是RDD的兩個API,而且cache底層調用的就是persist,區別之一就在於cache不能顯示指定緩存方式,只能緩存在內存中,可是persist能夠經過指定緩存方式,好比顯示指定緩存在內存中、內存和磁盤而且序列化等。經過RDD的緩存,後續能夠對此RDD或者是基於此RDD衍生出的其餘的RDD處理中重用這些緩存的數據集
容錯(checkpoint)
本質上是將RDD寫入磁盤作檢查點(一般是checkpoint到HDFS上,同時利用了hdfs的高可用、高可靠等特徵)。上面提到了Spark lineage,但在實際的生產環境中,一個業務需求可能很是很是複雜,那麼就可能會調用不少算子,產生了不少RDD,那麼RDD之間的linage鏈條就會很長,一旦某個環節出現問題,容錯的成本會很是高。此時,checkpoint的做用就體現出來了。使用者能夠將重要的RDD checkpoint下來,出錯後,只需從最近的checkpoint開始從新運算便可使用方式也很簡單,指定checkpoint的地址[SparkContext.setCheckpointDir("checkpoint的地址")],而後調用RDD的checkpoint的方法便可。
checkpoint與cache/persist對比
都是lazy操做,只有action算子觸發後纔會真正進行緩存或checkpoint操做(懶加載操做是Spark任務很重要的一個特性,不只適用於Spark RDD還適用於Spark sql等組件)
cache只是緩存數據,但不改變lineage。一般存於內存,丟失數據可能性更大
改變原有lineage,生成新的CheckpointRDD。一般存於hdfs,高可用且更可靠
RDD的依賴關係
Spark中使用DAG(有向無環圖)來描述RDD之間的依賴關係,根據依賴關係的不一樣,劃分爲寬依賴和窄依賴
經過上圖,能夠很容易得出所謂寬依賴:多個子RDD的partition會依賴同一個parentRDD的partition;窄依賴:每一個parentRDD的partition最多被子RDD的一個partition使用。這兩個概念很重要,像寬依賴是劃分stage的關鍵,而且通常都會伴有shuffle,而窄依賴之間其實就造成前文所述的pipeline管道進行處理數據。(圖中的map、filter等是Spark提供的算子,具體含義你們能夠自行到Spark官網瞭解,順便感覺一下scala函數式編程語言的強大)。
Spark任務以及stage等的具體劃分,牽涉到源碼,後續會單獨講解
最後筆者以RDD源碼中的註釋,闡述一下RDD的屬性:
1.分區列表(數據塊列表,只保存數據位置,不保存具體地址)
2. 計算每一個分片的函數(根據父RDD計算出子RDD)
3. RDD的依賴列表
4. RDD默認是存儲於內存,但當內存不足時,會spill到disk(可經過設置StorageLevel來控制)
5. 默認hash分區,可自定義分區器
6. 每個分片的優先計算位置(preferred locations)列表,好比HDFS的block的所在位置應該是優先計算的位置
關注微信公衆號:大數據學習與分享,獲取更對技術乾貨