雖然默認情況下 RDD 的內容是臨時的,但 Spark 提供了在 RDD 中持久化數據的機制。第一次調用動作並計算出 RDD 內容后,RDD 的內容可以存儲在集群的內存或磁盤上。這樣下一次需要調用依賴該 RDD 的動作時,就不需要從依賴關系中重新計算 RDD,數據可以從緩存分區中直接返回:
cached.cache()
cached.count()
cached.take(10)
在上述代碼中, cache 方法調用指示在下次計算 RDD 后,要把 RDD 存儲起來。調用count 會導致第一次計算 RDD。采取( take)這個動作返回一個本地的 Array,包含RDD 的前 10 個元素。但調用 take 時,訪問的是 cached 已經緩存好的元素,而不是從 cached 的依賴關系中重新計算出來的。
Spark 為持久化 RDD 定義了幾種不同的機制,用不同的 StorageLevel 值表示。 rdd.cache() 是 rdd.persist(StorageLevel.MEMORY) 的簡寫,它將 RDD 存儲為未序列化的 Java 對象。當 Spark 估計內存不夠存放一個分區時,它干脆就不在內存中存放該分區,這樣在下次需要時就必須重新計算。在對象需要頻繁訪問或低延訪問時適合使用StorageLevel.MEMORY,因為它可以避免序列化的開銷。相比其他選項, StorageLevel.MEMORY 的問題是要占用更大的內存空間。另外,大量小對象會對 Java 的垃圾回收造成壓力,會導致程序停頓和常見的速度緩慢問題。
Spark 也提供了 MEMORY_SER 的存儲級別,用於在內存中分配大字節緩沖區以存儲 RDD序列化內容。如果使用得當(稍后會詳細介紹),序列化數據占用的空間比未經序列化的數據占用的空間往往要少兩到五倍。
Spark 也可以用磁盤來緩存 RDD。存儲級別 MEMORY_AND_DISK 和 MEMORY_AND_DISK_SER分別類似於 MEMORY 和 MEMORY_SER。對於 MEMORY 和 MEMORY_SER,如果一個分區在內存里放不下,整個分區都不會放在內存。對於 MEMORY_AND_DISK 和 MEMORY_AND_DISK_SER,如果分區在內存里放不下, Spark 會將其溢寫到磁盤上。
什么時候該緩存數據是門藝術,這通常需要對空間和速度進行權衡,垃圾回收開銷的問題也會時不時讓情況更復雜。一般情況下,如果多個動作需要用到某個 RDD,而它的計算代價又很高,那么就應該把這個 RDD 緩存起來