zoukankan      html  css  js  c++  java
  • Spark之RDD容错原理及四大核心要点

    一、Spark RDD容错原理

      RDD不同的依赖关系导致Spark对不同的依赖关系有不同的处理方式。

      对于宽依赖而言,由于宽依赖实质是指父RDD的一个分区会对应一个子RDD的多个分区,在此情况下出现部分计算结果丢失,单一计算丢失的数据无法达到效果,便采用重新计算该步骤中的所有数据,从而会导致计算数据重复;对于窄依赖而言,由于窄依赖实质是指父RDD的分区最多被一个子RDD使用,在此情况下出现部分计算的错误,由于计算结果的数据只与依赖的父RDD的相关数据有关,所以不需要重新计算所有数据,只重新计算出错部分的数据即可。

    二、RDD容错的四大核心要点

      Spark框架层面的容错机制,主要分为三大层面(调度层、RDD血统层、Checkpoint层),在这三大层面中包括Spark RDD容错四大核心要点。

      (1)Stage输出失败,上层调度器DAGScheduler重试。
      (2)Spark计算中,Task内部任务失败,底层调度器重试。
      (3)RDD Lineage血统中窄依赖、宽依赖计算。
      (4)Checkpoint缓存。

    1.调度层(包含DAG生成和Task重算两大核心)

      从调度层面讲,错误主要出现在两个方面,分别是在Stage输出时出错和在计算时出错。

      1)DAG生成层

      Stage输出失败,上层调度器DAGScheduler会进行重试,DAGScheduler.scala的resubmitFailedStages的源码如下。

      /**
       * Resubmit any failed stages. Ordinarily called after a small amount of time has passed since
       * the last fetch failure.
       */
      private[scheduler] def resubmitFailedStages() {
        // 判断是否存在失败的Stages
        if (failedStages.size > 0) {
          // Failed stages may be removed by job cancellation, so failed might be empty even if
          // the ResubmitFailedStages event has been scheduled.
          // 失败的阶段可以通过作业取消删除,如果ResubmitFailedStages事件已调度,失败将是空值
          logInfo("Resubmitting failed stages")
          clearCacheLocs()
          // 获取所有失败Stage的列表
          val failedStagesCopy = failedStages.toArray
          // 清空failedStages
          failedStages.clear()
          // 对之前获取所有失败的Stage,根据jobId排序后逐一重试
          for (stage <- failedStagesCopy.sortBy(_.firstJobId)) {
            submitStage(stage)
          }
        }
        submitWaitingStages()
      }
    

      2)Task计算层

      Spark计算过程中,计算内部某个Task任务出现失败,底层调度器会对此Task进行若干次重试(默认4次)。TaskSetManager.scala的handleFailedTask的源码如下。

    /**
       * Marks the task as failed, re-adds it to the list of pending tasks, and notifies the
       * DAG Scheduler.
       */
      def handleFailedTask(tid: Long, state: TaskState, reason: TaskEndReason) {
    
        ......
    
        if (!isZombie && state != TaskState.KILLED
            && reason.isInstanceOf[TaskFailedReason]
            && reason.asInstanceOf[TaskFailedReason].countTowardsTaskFailures) {
          assert (null != failureReason)
          // 对失败的Task的numFailures进行计数加1
          numFailures(index) += 1
          // 判断失败的Task计数是否大于设定的最大失败次数,如果大于,则输出日志,并不再重试
          if (numFailures(index) >= maxTaskFailures) {
            logError("Task %d in stage %s failed %d times; aborting job".format(
              index, taskSet.id, maxTaskFailures))
            abort("Task %d in stage %s failed %d times, most recent failure: %s
    Driver stacktrace:"
              .format(index, taskSet.id, maxTaskFailures, failureReason), failureException)
            return
          }
        }
        // 如果运行的Task为0时,则完成Task步骤
        maybeFinishTaskSet()
      }
    

    2.RDD Lineage血统层容错

      Spark中RDD采用高度受限的分布式共享内存,且新的RDD的产生只能够通过其他RDD上的批量操作来创建,依赖于以RDD的Lineage为核心的容错处理,在迭代计算方面比Hadoop快20多倍,同时还可以在5~7s内交互式地查询TB级别的数据集。

      Spark RDD实现基于Lineage的容错机制,基于RDD的各项transformation构成了compute chain,在部分计算结果丢失的时候可以根据Lineage重新恢复计算。

      (1)在窄依赖中,在子RDD的分区丢失,要重算父RDD分区时,父RDD相应分区的所有数据都是子RDD分区的数据,并不存在冗余计算。
      (2)在宽依赖情况下,丢失一个子RDD分区,重算的每个父RDD的每个分区的所有数据并不是都给丢失的子RDD分区用的,会有一部分数据相当于对应的是未丢失的子RDD分区中需要的数据,这样就会产生冗余计算开销和巨大的性能浪费。

    3.checkpoint层容错

      Spark checkpoint通过将RDD写入Disk作检查点,是Spark lineage容错的辅助,lineage过长会造成容错成本过高,这时在中间阶段做检查点容错,如果之后有节点出现问题而丢失分区,从做检查点的RDD开始重做Lineage,就会减少开销。

      checkpoint主要适用于以下两种情况:

      (1)DAG中的Lineage过长,如果重算,开销太大,如PageRank、ALS等。
      (2)尤其适合在宽依赖上作checkpoint,这个时候就可以避免为Lineage重新计算而带来的冗余计算。

     

  • 相关阅读:
    陶哲轩实分析 习题 7.1.5
    java程序员必知的 8大排序
    java抽象类
    公式解析器开源项目整理
    大并发处理解决方案
    让Java代码跑得更快
    Java基础知识 (扫盲)
    一道多线程题目的解决方案
    如何优化JAVA程序设计和编码,提高JAVA性能
    词法分析(NFA与DFA)
  • 原文地址:https://www.cnblogs.com/xiaoyh/p/11070549.html
Copyright © 2011-2022 走看看