Spark最重要的一個功能是它可以通過各種操作(operations)持久化(或者緩存)一個集合到內存中。當你持久化一個RDD的時候,每一個節點都將參與計算的所有分區數據存儲到內存中,並且這些數據可以被這個集合(以及這個集合衍生的其他集合)的動作(action)重復利用。這個能力使后續的動作速度更快(通常快10倍以上)。對應迭代算法和快速的交互使用來說,緩存是一個關鍵的工具。
你能通過persist()
或者cache()
方法持久化一個rdd。首先,在action中計算得到rdd;然后,將其保存在每個節點的內存中。Spark的緩存是一個容錯的技術-如果RDD的任何一個分區丟失,它可以通過原有的轉換(transformations)操作自動的重復計算並且創建出這個分區。
此外,我們可以利用不同的存儲級別存儲每一個被持久化的RDD。例如,它允許我們持久化集合到磁盤上、將集合作為序列化的Java對象持久化到內存中、在節點間復制集合或者存儲集合到Tachyon中。我們可以通過傳遞一個StorageLevel
對象給persist()
方法設置這些存儲級別。cache()
方法使用了默認的存儲級別—StorageLevel.MEMORY_ONLY
。完整的存儲級別介紹如下所示:
Storage Level | Meaning |
---|---|
MEMORY_ONLY | 將RDD作為非序列化的Java對象存儲在jvm中。如果RDD不適合存在內存中,一些分區將不會被緩存,從而在每次需要這些分區時都需重新計算它們。這是系統默認的存儲級別。 |
MEMORY_AND_DISK | 將RDD作為非序列化的Java對象存儲在jvm中。如果RDD不適合存在內存中,將這些不適合存在內存中的分區存儲在磁盤中,每次需要時讀出它們。 |
MEMORY_ONLY_SER | 將RDD作為序列化的Java對象存儲(每個分區一個byte數組)。這種方式比非序列化方式更節省空間,特別是用到快速的序列化工具時,但是會更耗費cpu資源—密集的讀操作。 |
MEMORY_AND_DISK_SER | 和MEMORY_ONLY_SER類似,但不是在每次需要時重復計算這些不適合存儲到內存中的分區,而是將這些分區存儲到磁盤中。 |
DISK_ONLY | 僅僅將RDD分區存儲到磁盤中 |
MEMORY_ONLY_2, MEMORY_AND_DISK_2, etc. | 和上面的存儲級別類似,但是復制每個分區到集群的兩個節點上面 |
OFF_HEAP (experimental) | 以序列化的格式存儲RDD到Tachyon中。相對於MEMORY_ONLY_SER,OFF_HEAP減少了垃圾回收的花費,允許更小的執行者共享內存池。這使其在擁有大量內存的環境下或者多並發應用程序的環境中具有更強的吸引力。 |
NOTE:在python中,存儲的對象都是通過Pickle庫序列化了的,所以是否選擇序列化等級並不重要。
Spark也會自動持久化一些shuffle操作(如reduceByKey
)中的中間數據,即使用戶沒有調用persist
方法。這樣的好處是避免了在shuffle出錯情況下,需要重復計算整個輸入。如果用戶計划重用計算過程中產生的RDD,我們仍然推薦用戶調用persist
方法。
如何選擇存儲級別
Spark的多個存儲級別意味着在內存利用率和cpu利用效率間的不同權衡。我們推薦通過下面的過程選擇一個合適的存儲級別:
-
如果你的RDD適合默認的存儲級別(MEMORY_ONLY),就選擇默認的存儲級別。因為這是cpu利用率最高的選項,會使RDD上的操作盡可能的快。
-
如果不適合用默認的級別,選擇MEMORY_ONLY_SER。選擇一個更快的序列化庫提高對象的空間使用率,但是仍能夠相當快的訪問。
-
除非函數計算RDD的花費較大或者它們需要過濾大量的數據,不要將RDD存儲到磁盤上,否則,重復計算一個分區就會和重磁盤上讀取數據一樣慢。
-
如果你希望更快的錯誤恢復,可以利用重復(replicated)存儲級別。所有的存儲級別都可以通過重復計算丟失的數據來支持完整的容錯,但是重復的數據能夠使你在RDD上繼續運行任務,而不需要重復計算丟失的數據。
-
在擁有大量內存的環境中或者多應用程序的環境中,OFF_HEAP具有如下優勢:
-
它運行多個執行者共享Tachyon中相同的內存池
-
它顯著地減少垃圾回收的花費
-
如果單個的執行者崩潰,緩存的數據不會丟失
刪除數據
RDD.unpersist()
方法