1. 程式人生 > >【Spark】彈性分散式資料集RDD概述

【Spark】彈性分散式資料集RDD概述

彈性分佈資料集RDD

RDD(Resilient Distributed Dataset)是Spark的最基本抽象,是對分散式記憶體的抽象使用,實現了以操作本地集合的方式來操作分散式資料集的抽象實現。RDD是Spark最核心的東西,它表示已被分割槽,不可變的並能夠被並行操作的資料集合,不同的資料集格式對應不同的RDD實現。RDD必須是可序列化的。RDD可以cache到記憶體中,每次對RDD資料集的操作之後的結果,都可以存放到記憶體中,下一個操作可以直接從記憶體中輸入,省去了MapReduce大量的磁碟IO操作。這對於迭代運算比較常見的機器學習演算法, 互動式資料探勘來說,效率提升比較大。

你將RDD理解為一個大的集合,將所有資料都載入到記憶體中,方便進行多次重用。第一,它是分散式的,可以分佈在多臺機器上,進行計算。第二,它是彈性的,在計算處理過程中,機器的記憶體不夠時,它會和硬碟進行資料交換,某種程度上會減低效能,但是可以確保計算得以繼續進行。

RDD特性

RDD是分散式只讀且已分割槽集合物件。這些集合是彈性的,如果資料集一部分丟失,則可以對它們進行重建。具有自動容錯、位置感知排程和可伸縮性,而容錯性是最難實現的,大多數分散式資料集的容錯性有兩種方式:資料檢查點和記錄資料的更新。對於大規模資料分析系統,資料檢查點操作成本很高,主要原因是大規模資料在伺服器之間的傳輸帶來的各方面的問題,相比記錄資料的更新,RDD 也只支援粗粒度的轉換,也就是記錄如何從其它 RDD 轉換而來(即 Lineage),以便恢復丟失的分割槽。
其特性為:

  1. 資料儲存結構不可變
  2. 支援跨叢集的分散式資料操作
  3. 可對資料記錄按key進行分割槽
  4. 提供了粗粒度的轉換操作
  5. 資料儲存在記憶體中,保證了低延遲性

RDD的好處

  • RDD只能從持久儲存或通過Transformations操作產生,相比於分散式共享記憶體(DSM)可以更高效實現容錯,對於丟失部分資料分割槽只需根據它的lineage就可重新計算出來,而不需要做特定的Checkpoint。
  • RDD的不變性,可以實現類Hadoop MapReduce的推測式執行。
  • RDD的資料分割槽特性,可以通過資料的本地性來提高效能,這與Hadoop MapReduce是一樣的。
  • RDD都是可序列化的,在記憶體不足時可自動降級為磁碟儲存,把RDD儲存於磁碟上,這時效能會有大的下降但不會差於現在的MapReduce。

RDD程式設計介面

對於RDD,有兩種型別的動作,一種是Transformation,一種是Action。它們本質區別是:

Transformation返回值還是一個RDD。它使用了鏈式呼叫的設計模式,對一個RDD進行計算後,變換成另外一個RDD,然後這個RDD又可以進行另外一次轉換。這個過程是分散式的
Action返回值不是一個RDD。它要麼是一個Scala的普通集合,要麼是一個值,要麼是空,最終或返回到Driver程式,或把RDD寫入到檔案系統中

Transformations轉換操作,返回值還是一個 RDD,如 map、 filter、 union;
Actions行動操作,返回結果或把RDD持久化起來,如 count、 collect、 save。

RDD依賴關係

不同的操作依據其特性,可能會產生不同的依賴,RDD之間的依賴關係有以下兩種:

  • 窄依賴(Narrow Dependencies)
    一個父RDD分割槽最多被一個子RDD分割槽引用,表現為一個父RDD的分割槽;
    對應於一個子RDD的分割槽或多個父RDD的分割槽對應於一個子RDD的分割槽,也就是說一個父RDD的一個分割槽不可能對應一個子RDD的多個分割槽,如map、filter、union等操作則產生窄依賴;
  • 寬依賴(Wide Dependencies)
    一個子RDD的分割槽依賴於父RDD的多個分割槽或所有分割槽,也就是說存在一個父RDD的一個分割槽對應一個子RDD的多個分割槽,如groupByKey等操作則產生寬依賴操作;

下圖中,藍色實心方框代表一個partition,藍邊矩形框代表一個RDD:

Stage DAG

Spark提交Job之後會把Job生成多個Stage,多個Stage之間是有依賴的,Stage之間的依賴關係就構成了DAG(有向無環圖)。
對於窄依賴,Spark會盡量多地將RDD轉換放在同一個Stage中;而對於寬依賴,但大多數時候是shuffle操作,因此Spark會將此Stage定義為ShuffleMapStage,以便於向MapOutputTracker註冊shuffle操作。Spark通常將shuffle操作定義為stage的邊界。

RDD資料儲存管理

RDD可以被抽象地理解為一個大的陣列(Array),但是這個陣列是分佈在叢集上的。邏輯上RDD的每個分割槽叫一個Partition。
在Spark的執行過程中,RDD經歷一個個的Transfomation運算元之後,最後通過Action運算元進行觸發操作。 邏輯上每經歷一次變換,就會將RDD轉換為一個新的RDD,RDD之間通過Lineage產生依賴關係,這個關係在容錯中有很重要的作用。變換的輸入和輸出都是RDD。 RDD會被劃分成很多的分割槽分佈到叢集的多個節點中。分割槽是個邏輯概念,變換前後的新舊分割槽在物理上可能是同一塊記憶體儲存。 這是很重要的優化,以防止函式式資料不變性(immutable)導致的記憶體需求無限擴張。有些RDD是計算的中間結果,其分割槽並不一定有相應的記憶體或磁碟資料與之對應,如果要迭代使用資料,可以調cache()函式快取資料。

上圖中,RDD1含有5個分割槽(p1、 p2、 p3、 p4、 p5),分別儲存在4個節點(Node1、 node2、 Node3、 Node4)中。RDD2含有3個分割槽(p1、 p2、 p3),分佈在3個節點(Node1、 Node2、 Node3)中。

在物理上,RDD物件實質上是一個元資料結構,儲存著Block、 Node等的對映關係,以及其他的元資料資訊。一個RDD就是一組分割槽,在物理資料儲存上,RDD的每個分割槽對應的就是一個Block,Block可以儲存在記憶體,當記憶體不夠時可以儲存到磁碟上。
每個Block中儲存著RDD所有資料項的一個子集,暴露給使用者的可以是一個Block的迭代器(例如,使用者可以通過mapPartitions獲得分割槽迭代器進行操作),也可以就是一個數據項(例如,通過map函式對每個資料項平行計算)。本書會在後面章節具體介紹資料管理的底層實現細節。
如果是從HDFS等外部儲存作為輸入資料來源,資料按照HDFS中的資料分佈策略進行資料分割槽,HDFS中的一個Block對應Spark的一個分割槽。同時Spark支援重分割槽,資料通過Spark預設的或者使用者自定義的分割槽器決定資料塊分佈在哪些節點。例如,支援Hash分割槽(按照資料項的Key值取Hash值,Hash值相同的元素放入同一個分割槽之內)和Range分割槽(將屬於同一資料範圍的資料放入同一分割槽)等分割槽策略。