Spark Core 原理拆解:RDD 依赖关系与 DAG 调度机制深度解析
一、RDD 依赖关系原理
在 Spark 中,RDD(弹性分布式数据集)的依赖关系是任务调度的核心基础,主要分为两类:
-
窄依赖(Narrow Dependency)
- 定义:每个父 RDD 分区最多被子 RDD 的一个分区消费
- 数学表达:$ \text{ChildPartition}_i \subseteq \text{ParentPartition}_j $
- 特点:无需 Shuffle,可流水线执行
- 源码示例(
OneToOneDependency.scala):override def getParents(partitionId: Int): List[Int] = List(partitionId)
-
宽依赖(Shuffle Dependency)
- 定义:子 RDD 分区依赖多个父 RDD 分区
- 数学表达:$ \text{ChildPartition}i \subseteq \bigcup{k=1}^{n} \text{ParentPartition}_k $
- 特点:需要 Shuffle 操作,生成阶段边界
- 源码示例(
ShuffleDependency.scala):val shuffleHandle: ShuffleHandle = _rdd.context.env.shuffleManager.registerShuffle(...)
二、DAG 调度机制
DAG(有向无环图)调度器将逻辑执行计划转化为物理任务:
1. DAG 构建流程
graph LR
A[Action触发作业] --> B{DAGScheduler}
B --> C[解析RDD依赖链]
C --> D[识别宽依赖边界]
D --> E[划分Stage]
E --> F[生成TaskSet]
2. 阶段划分算法
- 阶段类型:
ShuffleMapStage:中间输出阶段ResultStage:最终结果阶段
- 划分规则:
- 从最终 RDD 反向遍历
- 遇到宽依赖即创建新阶段
- 源码关键函数(
DAGScheduler.scala):private def getParentStages(rdd: RDD[_]): List[Stage] = { val parents = new HashSet[Stage] val visited = new HashSet[RDD[_]] def visit(r: RDD[_]) { if (!visited(r)) { visited += r r.dependencies.foreach { case shufDep: ShuffleDependency[_, _, _] => parents += getShuffleMapStage(shufDep) case _ => visit(r) } } } visit(rdd) parents.toList }
3. 任务调度优化
- 延迟调度:基于数据本地性分配任务
- 推测执行:对慢任务启动备份任务
- 阶段合并:连续窄依赖合并为单一阶段
三、执行流程源码追踪
以 collect() 动作为例的调用链:
RDD.collect()触发作业提交DAGScheduler.handleJobSubmitted()创建最终阶段submitStage()递归提交父阶段submitMissingTasks()生成 TaskSetTaskScheduler.submitTasks()分配任务到 Executor
关键代码片段(精简):
// DAGScheduler.submitStage
private def submitStage(stage: Stage) {
if (!waiting(stage) && !running(stage) && !failed(stage)) {
val missing = getMissingParentStages(stage).sortBy(_.id)
if (missing.isEmpty) {
submitMissingTasks(stage) // 提交任务
} else {
missing.foreach(submitStage) // 递归提交父阶段
}
}
}
四、容错机制设计
- 阶段重算:
- 利用 RDD 的血缘关系(Lineage)
- 仅重算丢失分区的父阶段
- Shuffle 容错:
- 通过
MapOutputTracker记录输出位置 - 源码容错处理:
case ef: ExceptionFailure => scheduler.handleTaskFailed(...)
- 通过
五、性能优化启示
- 避免不必要的宽依赖
- 使用
persist()减少重算 - 控制分区数量平衡负载
- 利用
reduceByKey替代groupByKey
通过理解 RDD 依赖与 DAG 调度机制,可有效优化 Spark 应用性能。源码分析建议重点关注
DAGScheduler.scala和ShuffleDependency.scala模块。
&spm=1001.2101.3001.5002&articleId=154127370&d=1&t=3&u=1eda65e21286421c8dcf619b9ebf9045)
321

被折叠的 条评论
为什么被折叠?



