checkpoint
Checkpoint() :
说明:数据存在本地。该函数会创建个二进制文件,存在checkpoint目录。不是action,不会马上执行,配合 sc.setCheckpointDir(“/data/checkpoint”)使用.
使用该函数之前先 persist ,否则该函数会重新计算。
使用场景: 当下游 RDD 计算出错时,可以直接从 checkpoint 过的 RDD 那里读取数据继续算。用于运算消耗很大的rdd计算,计算后执行下 checkpoint()。
sc.setCheckpointDir()
然后在执行rdd_A.checkpoint():
rdd_A.checkpoint()
{
if (checkpointData.isEmpty) {
// 初始化 checkpointData 对象,作标记, RDDCheckpointData 用于管理rdd的checkpoint相关
checkpointData = Some(new ReliableRDDCheckpointData(this))
}
}
执行rdd_A.checkpoint()并不会让rdd_A马上持久化,直到action才执行持久化。
rdd_C.count() // 执行action
{
SparkContext.runJob()
{
dagScheduler.runJob(..)
rdd.doCheckpoint() // 在runJob后执行
{
if (!doCheckpointCalled) { // doCheckpointCalled标记确保下面if内容只执行一次
doCheckpointCalled = true
if (checkpointData.isDefined) { // 判断rdd是否需要checkpoint
checkpointData.get.checkpoint() // 保存数据
} else {
dependencies.foreach(_.rdd.doCheckpoint()) //向上遍历父rdd判断是否需要checkpoint
}
}
}
}
}
iterator 函数
在任务执行时会调用该函数,轨迹为:
ShuffleMapTask.runTask()
{
val (rdd, dep) = ser.deserialize[(RDD[_], ShuffleDependency[_, _, _])](...)
...
writer.write(rdd.iterator(partition, context).asInstanceOf[Iterator[_ <: Product2[Any, Any]]]) // 这里执行了rdd.iterator()...
}
iterator 函数实现大体是这么个流程:
1 若标记了有缓存,则取缓存,取不到则进行”计算或读检查点”。完了再存入缓存,以备后续使用。
2 若未标记有缓存,则直接进行”计算或读检查点”。
3 “计算或读检查点”这个过程也做两个判断:有做过checkpoint,没有做过checkpoint。做过checkpoint则可以读取到检查点数据返回。无则调该rdd的实现类的computer函数计算。computer函数实现方式就是向上递归“获取父rdd分区数据进行计算”,直到遇到检查点rdd获取有缓存的rdd。
iterator 函数:
final def iterator(split: Partition, context: TaskContext): Iterator[T] = {
if (storageLevel != StorageLevel.NONE) { // 该rdd设置了缓存
SparkEnv.get.cacheManager.getOrCompute(this, split, context, storageLevel)
{
//从缓存取数据,娶不到就计算
blockManager.get(key) match {
case Some(blockResult) =>
case None =>
val computedValues = rdd.computeOrReadCheckpoint(partition, context) //计算
if (context.isRunningLocally) { return computedValues } // 如果是本地模式,就不缓存了 ?????
val cachedValues = putInBlockManager(key, computedValues, storageLevel, updatedBlocks) // 放入缓存
}
}
} else {
computeOrReadCheckpoint(split, context)
{
// def isCheckpointed: Boolean = checkpointData.exists(_.isCheckpointed) ,checkpointData 类型为 RDDCheckpointData
// 判断 rdd是否执行过了 rdd.doCheckpoint()
if (isCheckpointed)
{
// 如果设置了检查点,则对父rdd进行 iterator() => computeOrReadCheckpoint() => ReliableCheckpointRDD.compute()
// 这样则可以直接从hdfs直接读取当前rdd数据,而无需在执行rdd.compute();
firstParent[T].iterator(split, context)
} else {
// 各个子类rdd需实现compute函数
// 比如在 MapPartitionsRDD ,rdd.compute()执行向上追溯递归计算,直到碰到缓存rdd或者存有checkpoint的rdd才停止向上追溯。
// 如果rdd为checkpointRdd, 则执行 ReliableCheckpointRDD.compute,从检查点读
compute(split, context)
}
}
}
}
MapPartitionsRDD 中compute():
private[spark] class MapPartitionsRDD[U: ClassTag, T: ClassTag](
prev: RDD[T],
f: (TaskContext, Int, Iterator[T]) => Iterator[U], // (TaskContext, partition index, iterator)
preservesPartitioning: Boolean = false){
...
override def compute(split: Partition, context: TaskContext): Iterator[U] =
f(context, split.index, firstParent[T].iterator(split, context))
}
ReliableCheckpointRDD 中compute():
private[spark] class ReliableCheckpointRDD[T: ClassTag](
@transient sc: SparkContext,
val checkpointPath: String)
extends CheckpointRDD[T](sc) {
...
override def compute(split: Partition, context: TaskContext): Iterator[T] = {
val file = new Path(checkpointPath, ReliableCheckpointRDD.checkpointFileName(split.index))
ReliableCheckpointRDD.readCheckpointFile(file, broadcastedConf, context)
}
}