前面已經介紹了persist和cache的基本原理。那麼RDD是如何進行persist和cache操作的呢?
Persist RDD如何執行
從實現層面,persist()函數只是對RDD的storageLevel(存儲級別)進行了設置,函數內部並沒有執行任何數據緩存的動作。那麼,當設置完RDD的storageLevel後,RDD的緩存動作是如何觸發的,又是如何實現的呢?
首先,在抽象類RDD中,定義了一個沒有實現的函數compute,該函數用來計算RDD的一個分區。該函數由各種類型的RDD(RDD的子類)來實現,因爲每種類型的RDD計算方式不同。該函數的定義如下:
def compute(split: Partition, context: TaskContext): Iterator[T]
另外,在RDD抽象類中還提供的一個迭代器函數:
final def iterator(split: Partition, context: TaskContext): Iterator[T] = {
// 若設置了RDD的存儲級別:storageLevel,從緩存中讀取或則重新計算
if (storageLevel != StorageLevel.NONE) {
getOrCompute(split, context)
} else {
// 默認是存儲級別是NONE,調用下面的函數來重新計算RDD,或從checkpoint讀取數據
computeOrReadCheckpoint(split, context)
}
}
getOrCompute函數
getOrCompute函數會根據StorageLevel先從本地的存儲中讀取分區數據,若讀取失敗,則調用compute函數來計算分區數據。
/**
* Compute an RDD partition or read it from a checkpoint if the RDD is checkpointing.
*/
private[spark] def computeOrReadCheckpoint(split: Partition, context: TaskContext): Iterator[T] =
{
// 若checkpoint中存在該分區,則直接讀取該分區的數據,調用compute函數計算該分區數據
if (isCheckpointedAndMaterialized) {
firstParent[T].iterator(split, context)
} else {
compute(split, context)
}
}
該函數用來讀取或計算RDD的分區。該函數的執行步驟如下:
- 先從本地獲取分區數據,若本地不存在該分區的數據則會通過傳輸服務從遠端節點獲取分區數據。
- 若從本地或遠端沒有獲取到數據,則調用computeOrReadCheckpoint函數從checkpoint數據保存目錄獲取數據,若沒有進行checkpoint則調用compute來計算。
Persist實戰
數據準備
scala> val rdd = sc.parallelize(Seq("Hello World", "Just doit"))
scala> val mapres = rdd.map(x => x.split(" "))
scala> mapres.collect()
cache和persist實戰
scala> mapres.getStorageLevel.description
res1: String = Serialized 1x Replicated
scala> mapres.getStorageLevel.toString
res2: String = StorageLevel(1 replicas)
# 進行persist操作,其實就是進行cache操作
scala> mapres.persist()
res3: mapres.type = MapPartitionsRDD[1] at map at <console>:25
# 再次查看rdd的狀態,可以看到其狀態改了,存儲級別是memory了
scala> mapres.getStorageLevel.toString
res4: String = StorageLevel(memory, deserialized, 1 replicas)
# 修改StoreageLevel
scala> import org.apache.spark.storage.StorageLevel
import org.apache.spark.storage.StorageLevel
# 修改存儲級別時報錯了。
# 可見,在同一個SparkContext下,若設置了RDD的存儲級別,則無法修改
scala> mapres.persist(StorageLevel.MEMORY_AND_DISK)
java.lang.UnsupportedOperationException: Cannot change storage level of an RDD after it was already assigned a level
at org.apache.spark.rdd.RDD.persist(RDD.scala:170)
at org.apache.spark.rdd.RDD.persist(RDD.scala:195)
... 49 elided