在 Spark 中,RDD checkpoint 是通过启动两个独立的 Job 完成的。这两个 Job 分别用于生成 checkpoint 数据和更新依赖关系。下面从源码角度深入分析这个机制。
1. 为什么需要两个 Job?
当调用 RDD.checkpoint()
后:
- 第一个 Job:将 RDD 的每个分区数据计算后,写入到指定的 checkpoint 存储位置(如 HDFS)。这个步骤的目的是将 RDD 数据物化为可靠存储,减少后续计算的成本。
- 第二个 Job:在 checkpoint 成功完成后,更新 RDD 的依赖关系,将原始的血缘依赖(lineage)替换为从 checkpoint 存储加载数据的依赖。这个步骤的目的是确保后续的计算直接基于 checkpoint 数据,而不是重新计算血缘链。
这两个 Job 是独立的,且按顺序执行,确保 checkpoint 的一致性。
2. 源码分析
以下是 Spark RDD checkpoint 的源码路径和执行过程分析。
2.1 RDD.checkpoint() 的入口
调用 RDD.checkpoint()
方法时:
def checkpoint(): Unit = {
if (!isCheckpointedAndMaterialized) {
sc.checkpointFile[RDD类型](this)
}
}
此方法会:
- 检查是否已经 checkpointed,如果是,直接返回。
- 如果没有,则调用
SparkContext
的checkpointFile
方法,提交一个任务将数据写入存储。