博客地址: http://blog.csdn.net/yueqian_zhu/
RDD有cache和persist方法,用於將RDD進行緩存。
cache方法其實就是persist(MEMORY_ONLY)
我們看一下這個方法幹了什麼事情
def persist(newLevel: StorageLevel): this.type = {
// TODO: Handle changes of StorageLevel
if (storageLevel != StorageLevel.NONE && newLevel != storageLevel) {
throw new UnsupportedOperationException(
"Cannot change storage level of an RDD after it was already assigned a level")
}
sc.persistRDD(this)
// Register the RDD with the ContextCleaner for automatic GC-based cleanup
sc.cleaner.foreach(_.registerRDDForCleanup(this))
storageLevel = newLevel
this
}
/**
* Register an RDD to be persisted in memory and/or disk storage
*/
private[spark] def persistRDD(rdd: RDD[_]) {
_executorAllocationManager.foreach { _ =>
logWarning(
s"Dynamic allocation currently does not support cached RDDs. Cached data for RDD " +
s"${rdd.id} will be lost when executors are removed.")
}
persistentRdds(rdd.id) = rdd
}
相當簡單,只是將我們設定的level記錄到storageLevel中,以及保存這個rdd及rddId的映射到persistentRdds中,這裏還看不出有什麼作用!
以前講到過,action觸發後,會執行iterator方法從而獲得指定分區的數據迭代器。
final def iterator(split: Partition, context: TaskContext): Iterator[T] = {
if (storageLevel != StorageLevel.NONE) {
SparkEnv.get.cacheManager.getOrCompute(this, split, context, storageLevel)
} else {
computeOrReadCheckpoint(split, context)
}
}
如果這個rdd之前我們設置過cache或者persist,這裏就執行if語句中的內容。
如果沒有設置過,才從checkpoint中讀取;如果沒有checkpoint,才重新計算。
if語句中的代碼比較多,就不貼代碼純發揮口才了。。
1、用rddID和partition組合成RDDBlockId作爲blockmanager中的key,我們就是根據這個key從blockmanager中獲取數據(如果之前有緩存的話)
2、如果沒有數據,就執行計算,然後將結果存到blockmanager中。
3、如果設置的level不是存內存的,就調用blockmanager的putIterator方法真正存儲數據。
4、如果設置的level是存內存的,且可以全部放入內存中,那就這麼幹吧;如果不能全部放入內存中,且也設置了Disk存儲,就全部放入Disk中,否則就不存了唄
在需要做checkpoint之前,需要調用RDD.checkpoint()來mark一下先,不然是不起作用的。並且最好選擇persist這個RDD,不然的話會重新計算之前的所有rdd,因爲checkpoint操作也是一個新的runJob的過程。
在SparkContext.runJob中, 最後會調用rdd.doCheckpoint() ,所以,mark必須在run job之前完成。