[scala-spark]8. RDD的實現和編程接口

1. RDD的實現

  • 作業調度

當對RDD執行“轉換操作”時,調度器(DGAScheduler)會根據RDD的血統來構建由若干調度階段(State)組成的有向無環圖(DAG),每個調度階段包含儘可能多的連續“窄依賴”轉換。調度器按照有向無環圖順序進行計算,並最終得到目標RDD。

調度器(TaskScheduler)向各節點分配任務採用延時調度機制並根據數據存儲位置來確定(數據本地性:移動計算而非移動數據)。若一個任務需要處理的某個分區剛好存儲在某個節點的內存中,則該任務會分配給該節點;如果在內存中不包含該分區,調度器會找到包含該RDD的最佳位置,並把任務分配給所在節點。

  • 內存管理

Spark從大的方向上提供了兩種持久化RDD的存儲策略:一是存在內存中;二是存儲在磁盤中。

對於內存使用LRU回收算法來進行管理,當計算得到一個新的RDD分區,但沒有足夠空間來存儲時,系統會從最近最少使用的RDD中回收其一個分區的空間。除非該RDD是新分區對應的RDD,這種情況下Spark會將舊的分區繼續保留在內存中,防止同一個RDD的分區被循環調入和調出。這點很關鍵,因爲大部分的操作會在一個RDD的所有分區上進行,那麼很有可能已經存在內存中的分區將再次被使用。

  • 檢查點支持

雖然血統可以用於錯誤後RDD的恢復,但是對於很長的血統的RDD來說,這樣的恢復耗時比較長,因此需要通過檢查點操作保存到外部存儲中。

Spark提供爲RDD設置檢查點操作的API,可以讓用戶自行決定需要爲哪些數據設置檢查點操作。另外由於RDD的只讀特性,使得不需要關心數據一致性問題。

2. RDD編程接口

Spark中提供了通用接口來抽象每個RDD,這些接口包括:

  1. 分區信息:它們是數據集的最小分片。
  2. 依賴關係:指向其父RDD。
  3. 函數:基於父RDD計算方法。
  4. 劃分策略和數據位置的元數據。
  • RDD分區-partitions

一個RDD劃分成很多的分區(partition)分佈在集羣的節點中,分區的多少涉及對這個RDD進行並行計算的粒度。在RDD操作中用戶可以使用partitions方法獲取RDD劃分的分區數,當然用戶也可以設定分區數目。如果沒有指定將使用默認值,而默認數值是該程序所分配到CPU核數,如果是從HDFS文件創建,默認爲文件的block數(有一點我們必須要注意,當我們顯示的設置分區數時,分區數不允許小於HDFS文件的block數)。

/ 使用textFile方法獲取指定路徑的文件,未設置分區數
val rdd = sc.textFile("/app/spark/workcount.txt")
// 使用partitions方法獲取分區數,假設默認的分區數爲2,那麼將返回2
val partitionSize = rdd.partitions.size
 
// 顯示地設置RDD爲6個分區
rdd = sc.textFile("/app/spark/wordcount.txt", 6)
// 獲取分區數,此時返回6
partitionSize = rdd.partitions.size
  • RDD首選位置(preferred locations)

在Spark形成任務有向無環圖(DAG)時,會儘可能地把計算分配到靠近數據的位置,減少數據網絡傳輸。當RDD產生的時候存在首選位置,如HadoopRDD分區的首選位置就是HDFS塊所在的節點。當RDD分區被緩存,則計算應該發送到緩存分區所在的節點進行,再不然回溯RDD的血統,一直找到具有首選位置屬性的父RDD,並據此決定子RDD的位置。 

  • RDD依賴關係

在RDD中將依賴關係分成了兩種類型:窄依賴(Narrow Dependencies)和寬依賴(Wide Dependencies)。其中窄依賴是指每個父RDD的分區都至多被一個子RDD的分區使用,而寬依賴是多個子RDD的分區依賴一個父RDD分區。 這兩種依賴的區別從兩個方面來說比較有用。第一:窄依賴允許在單個集羣節點上流水線式執行,這個節點可以計算所有父級分區。相反,寬依賴需要所有的父RDD數據可用,並且數據已經通過Shuffle完成。第二:在窄依賴中,節點的恢復更加高效,因爲只有丟失的父級分區需要重新計算,並且這些丟失的父級分區可以並行地在不同節點上重新計算。相反,在寬依賴的繼承關係中,單個失敗的節點可能導致一個RDD的所有先祖RDD中的一些分區丟失,導致計算的重新執行。

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