Spark RDD Persistence

Spark最爲重要的特性之一就是可以在多個操作(Action)之間,將一個或多個RDD關聯的數據集(Dataset)以分區(Partition)爲單位進行持久化(Persist)或緩存(Cache),存儲介質通常是內存(Memory)。
被持久化或緩存的RDD A可以在兩種情況下被很好地“重複”利用:
(1)直接依賴:操作(Action)直接應用於RDD A之上;
(2)間接依賴:操作(Action)間接應用於RDD B之上,而RDD B來源於RDD A;
持久化或緩存是迭代式計算和交互式應用的關鍵技術,通常可以提升10位以上的計算速度。
實際應用中,RDD的持久化或緩存選項是通過persist()或cache()發出的,之後如果某個操作(Action)觸發該RDD的數據第一次被計算,那麼計算的結果數據(也就是該RDD的數據)就會以分區的形式被緩存於計算節點的內存中;而且這些數據是可以實現容錯的,如果這個RDD的某些分區數據丟失(因爲節點故障),這些分區的數據可以在使用時通過世代信息(Lineage)被自動恢復。
RDD的存儲形式或存儲介質是可以通過存儲級別(Storage Level)被定義的。例如,將數據持久化到磁盤、將Java對象序列化之後(有利於節省空間)緩存至內存、開啓複製(RDD的分區數據可以被備份到多個節點防止丟失)或者使用堆外內存(Tachyon)。persist()可以接收一個StorageLevel對象(Scala、Java、Python)用以定義存儲級別,如果使用的是默認的存儲級別(StorageLevel.MEMORY_ONLY),Spark提供了一個便利方法:cache()。
存儲級別選項如下:
MEMORY_ONLY 默認選項,RDD的(分區)數據直接以Java對象的形式存儲於JVM的內存中,如果內存空間不足,某些分區的數據將不會被緩存,需要在使用的時候根據世代信息重新計算。
MYMORY_AND_DISK RDD的數據直接以Java對象的形式存儲於JVM的內存中,如果內存空間不中,某些分區的數據會被存儲至磁盤,使用的時候從磁盤讀取。
MEMORY_ONLY_SER RDD的數據(Java對象)序列化之後存儲於JVM的內存中(一個分區的數據爲內存中的一個字節數組),相比於MEMORY_ONLY能夠有效節約內存空間(特別是使用一個快速序列化工具的情況下),但讀取數據時需要更多的CPU開銷;如果內存空間不足,處理方式與MEMORY_ONLY相同。
MEMORY_AND_DISK_SER 相比於MEMORY_ONLY_SER,在內存空間不足的情況下,將序列化之後的數據存儲於磁盤。
DISK_ONLY 僅僅使用磁盤存儲RDD的數據(未經序列化)。
MEMORY_ONLY_2,
MEMORY_AND_DISK_2, etc.
以MEMORY_ONLY_2爲例,MEMORY_ONLY_2相比於MEMORY_ONLY存儲數據的方式是相同的,不同的是會將數據備份到集羣中兩個不同的節點,其餘情況類似。
OFF_HEAP(experimental) RDD的數據序例化之後存儲至Tachyon。相比於MEMORY_ONLY_SER,OFF_HEAP能夠減少垃圾回收開銷、使得Spark Executor更“小”更“輕”的同時可以共享內存;而且數據存儲於Tachyon中,Spark集羣節點故障並不會造成數據丟失,因此這種方式在“大”內存或多併發應用的場景下是很有吸引力的。需要注意的是,Tachyon並不直接包含於Spark的體系之內,需要選擇合適的版本進行部署;它的數據是以“塊”爲單位進行管理的,這些塊可以根據一定的算法被丟棄,且不會被重建。
注意:使用PySpark(即使用Python開發Spark應用程序)時,所有需要存儲的數據都會使用Pickle進行序列化,這種行爲與存儲級別無關。
Spark推薦用戶將需要重複使用的RDD通過persist()或cache()顯式持久化。同時我們需要知道,會觸發“Shuffle”的操作是特殊的,例如reduceByKey,即使沒有用戶的顯式persist,它也會自動持久化“Shuffle”的中間結果,以防止“Shuffle”過程中某些節點故障導致整個輸入數據被重新計算。
那麼我們應該如何選取持久化的存儲級別呢?實際上存儲級別的選取就是Memory與CPU之間的雙重權衡,可以參考下述內容:
(1)如果RDD的數據可以很好的兼容默認存儲級別(MEMORY_ONLY),那麼優先使用它,這是CPU工作最爲高效的一種方式,可以很好地提高運行速度;
(2)如果(1)不能滿足,則嘗試使用MEMORY_ONLY_SER,且選擇一種快速的序列化工具,也可以達到一種不錯的效果;
(3)一般情況下不要把數據持久化到磁盤,除非計算是非常“昂貴”的或者計算過程會過濾掉大量數據,因爲重新計算一個分區數據的速度可能要高於從磁盤讀取一個分區數據的速度;
(4)如果需要快速的失敗恢復機制,則使用備份的存儲級別,如MEMORY_ONLY_2、MEMORY_AND_DISK_2;雖然所有的存儲級別都可以通過重新計算丟失的數據實現容錯,但是備份機制使得大部分情況下應用無需中斷,即數據丟失情況下,直接使用備份數據,而不需要重新計算數據的過程;
(5)如果處於大內存或多應用的場景下,OFF_HEAP可以帶來以下的好處:
a. 它允許Spark Executors可以共享Tachyon的內存數據;
b. 它很大程序上減少JVM垃圾回收帶來的性能開銷;
c. Spark Executors故障不會導致數據丟失。
最後,Spark可以自己監測“緩存”空間的使用,並使用LRU算法移除舊的分區數據。我們也可以通過顯式調用RDD unpersist()手動移除數據。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章