DAGScheduler详解

DAGScheduler是Spark的高级调度器,负责根据RDD依赖关系形成DAG,并将Job划分为Stage。Stage是任务执行的单位,分为ResultStage和ShuffleMapStage。在提交Job时,DAGScheduler进行Stage划分,包括ResultStage创建、获取或创建父Stage列表、提交Stage等步骤。当所有依赖Stage完成时,提交TaskSet给TaskScheduler执行。
摘要由CSDN通过智能技术生成


DAGScheduler思维导图

概述

在spark中调度器(Scheduler)有两种:

  • TaskScheduler(低级的调度器接口):负责实际每个具体Task的物理调度执行。
  • DAGScheduler(高级的调度器接口):负责将Task拆分成不同Stage的具有依赖关系(包含RDD的依赖关系)的多批任务,然后提交给TaskScheduler进行具体处理。

DAGScheduler会根据各个RDD之间的依赖关系形成一个DAG,并根据ShuffleDependency来进行stage的划分。stage包含多个tasks,个数由该stage的finalRDD决定,stage里面的task完全相同。DAGScheduler完成stage的划分后基于每个Stage生成TaskSet,并提交给TaskScheduler。TaskScheduler负责具体的task的调度,在Executor上启动执行task。

spark job任务拆分图

基本概念

  • Job:提交给调度器的顶层工作组件,一次RDD Action生成的一个或多个Stage所组成的一次计算作业。举例说明,当用户调用一个action操作,如count,则一个Job会通过submitJob方式被提交。每个Job可能需要执行多个stage来生成中间和结果数据。
  • Stage:用来计算Job中间和结果数据的tasks的集合,其中每个task在同一个RDD的不同partition上执行同样的函数。Stages是在shuffle边缘被分离,这会产生一个依赖等待(即必须等待前一个stage完成后才能拉取其输出数据)。有两种类型的stages:ResultStage是执行一个action操作的最后的一个stage,用来生成最终结果;而ShuffleMapStage会为一个shuffle操作生成map输出文件,即中间数据。当这些jobs重用同一个RDD,则Stages会在多个jobs间共享。
  • Task:在集群上运行的基本单位。一个Task负责处理RDD的一个partition。RDD的多个patition会分别由同一个TaskSet的不同Task去处理。Tasks是独立工作的单元,每个将会被发送到一台机器上执行。
  • TaskSet任务集:一组关联的,但是互相之间没有Shuffle依赖关系的任务所组成的任务集,一个stage对应生成一个TaskSet任务集。
  • Cache跟踪:DAGScheduler找到哪些RDDs已经被cache了来避免重计算它们,而且同样地记住哪些ShuffleMapStages已经生成了输出文件来避免重建一个shuffle的map侧计算任务。
  • Preferred locations:优化的任务计算位置,DAGScheduler同样基于潜在RDDs的最优化位置,或缓存及shuffle数据的位置,来计算在哪里执行stage中的每个task。
  • Cleanup:所有数据结构会被清除当依赖于它们运行的jobs完成后,以防止在一个长期运行的应用中内存泄漏。

主要功能

  1. 接收用户提交的job;
  2. 将job划分为不同stage的DAG图,记录哪些RDD、Stage被物化存储,并在每一个stage内产生一系列的task,并封装成TaskSet;
  3. 结合当前的缓存情况,决定每个Task的最佳位置(移动计算而不是移动数据,任务在数据所在的节点上运行),将TaskSet提交给TaskScheduler;
  4. 重新提交Shuffle输出丢失的Stage给TaskScheduler(不是由shuffle输出丢失造成的Stage内部的错误,DAGScheduler是不管的,由TaskScheduler负责尝试重新提交task执行);

DAGScheduler类说明

SparkContext中创建DAGScheduler的代码如下,创建DAGScheduler时将SparkContext自身的引用传递进去了。

// 为什么用volatile修饰?并行提交job时,保持多线程内的可见性?
@volatile private var _dagScheduler: DAGScheduler = _
_dagScheduler = new DAGScheduler(this)

由DAGScheduler类定义可以看出DAGScheduler持有了SparkContext,TaskScheduler以及MapOutTrackerMaster和BlockManagerMaster等。DAGScheduler的数据结构主要维护jobId和stageId的关系、Stage、ActiveJob,以及缓存的RDD的partitions的位置信息等。

private[spark] class DAGScheduler(
    private[scheduler] val sc: SparkContext,
    private[scheduler] val taskScheduler: TaskScheduler,
    listenerBus: LiveListenerBus,
    mapOutputTracker: MapOutputTrackerMaster,
    blockManagerMaster: BlockManagerMaster,
    env: SparkEnv,
    clock: Clock = new SystemClock())
  extends Logging {

  // 用于生成jobId(taskId是在taskschedulerImpl中生成,shuffleId和RddId在SparkContext中生成)
  private[scheduler] val nextJobId = new AtomicInteger(0)
  // 生成stageId
  private val nextStageId = new AtomicInteger(0)
  // jobid到Stage id集合的映射
  private[scheduler] val jobIdToStageIds = new HashMap[Int, HashSet[Int]]
  // StageId到stage的映射
  private[scheduler] val stageIdToStage = new HashMap[Int, Stage]
  /**
   * 来自shuffle dependency ID到ShuffleMapStage的映射,将会生成对应依赖的数据。只包含当前运行job部分的
   * stage(当jobs要求的shuffle stage完成后,这个映射将会被移除,并且这个shuffle数据的唯一记录将会
   * 被放在MapOutputTracker中)。
   */
  private[scheduler] val shuffleIdToMapStage = new HashMap[Int, ShuffleMapStage]
  // jobId对应的ActiveJob(正在运行的job)集合
  private[scheduler] val jobIdToActiveJob = new HashMap[Int, ActiveJob]

  // 需要运行的Stage的集合,依赖stage还没有完成的stage集合
  private[scheduler] val waitingStages = new HashSet[Stage]

  // 正在运行的Stage集合
  private[scheduler] val runningStages = new HashSet[Stage]

  // 由于拉取数据失败而需要重新提交的stage集合
  private[scheduler] val failedStages = new HashSet[Stage]

  private[scheduler] val activeJobs = new HashSet[ActiveJob]

  // 包含每个RDD的partitions被缓存的位置。
  // 这个映射的key是RDD的id,而它的value是用partition分区值索引的数组。
  // 数组的每个值是缓存的RDD分区partition的位置集合。
  // 所以访问这个映射的操作应该被用synchronizing进行监控(具体查看问题SPARK-4454)。
  private val cacheLocs = new HashMap[Int, IndexedSeq[Seq[TaskLocation]]]

  // 为了跟踪记录失败的结点,我们使用MapOutputTracker的时间值,它和每个task一起发送。
  // 当我们检测到一个结点失败,我们记录下当前的时间值和失败的executor,为新tasks增加它的值,并利用它来忽视
  // 丢失的ShuffleMapTask的结果。
  // TODO: 关于失败时间点的垃圾收集信息,当我们知道没有更多的丢失的消息需要被检测。
  private val failedEpoch = new HashMap[String, Long]

  // 是一个典型的生产者和消费者模式,提供事件的缓冲与异步处理
  // 为什么不在dagscheduler中直接生成缓冲队列?因为用一个EventLoop抽象类定义缓冲队列
  // 的模板,而在子类中具体实现onReceive方法,实现具体的实现,这样可扩展性强
  // 这样就不用在想扩展事件处理方法时去继承DAGScheduler类,更加灵活方便
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值