spark rdd的iterator()计算实现以及checkpoint源码

本文详细介绍了 Apache Spark 中的 Checkpoint 机制,包括其工作原理、如何设置和使用,以及 Checkpoint 在故障恢复中的作用。同时探讨了 Checkpoint 与 RDD 的缓存机制之间的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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)
      }
  }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值