1. 核心调度组件
-
DAGScheduler:负责将Job拆分为Stage,处理Stage间的依赖关系。
-
TaskScheduler:将Task分配到Executor,监控任务执行。
-
SchedulerBackend:与集群管理器(如YARN、K8s)通信,管理Executor资源。
2. 调度流程分步拆解
步骤1:用户提交代码
val rdd = sc.textFile("hdfs://data.txt").flatMap(_.split(" ")).map((_, 1)).reduceByKey(_ + _)
rdd.collect() // 触发Job提交
步骤2:生成DAG(有向无环图)
-
RDD血缘(Lineage):记录RDD的转换过程(
textFile
→flatMap
→map
→reduceByKey
)。 -
宽依赖(Shuffle):
reduceByKey
导致Stage划分。
步骤3:划分Stage
-
Stage 0:
textFile
→flatMap
→map
(窄依赖,合并为一个Stage)。 -
Stage 1:
reduceByKey
(宽依赖,单独一个Stage)。
步骤4:提交Task
-
Stage 0生成多个
MapTask
,Stage 1生成多个ReduceTask
。 -
TaskScheduler根据数据本地性(Data Locality)分配Task到Executor。
步骤5:执行与监控
-
Executor执行Task,向Driver汇报状态。
-
失败Task自动重试(默认重试3次)。
3. 关键概念详解
概念 | 说明 | 示例 |
---|---|---|
Job | 由行动操作(如collect )触发的完整计算任务 | 一次collect() 生成一个Job |
Stage | 由一组无Shuffle依赖的Task组成(分为ResultStage 和ShuffleMapStage ) | reduceByKey 前为一个Stage |
Task | Stage中每个分区的计算单元(ShuffleMapTask 或ResultTask ) | 处理一个分区的数据 |
Shuffle | 跨Stage数据重分布(如groupByKey 、join ) | reduceByKey 触发Shuffle |
数据本地性 | 优先将Task调度到数据所在节点(PROCESS_LOCAL > NODE_LOCAL > ANY ) | 读取HDFS块时优先分配到数据所在节点 |
4. 调度流程示意图
5. 性能优化点
-
减少Shuffle:
-
用
reduceByKey
替代groupByKey
(提前局部聚合)。 -
使用
Broadcast Join
代替Shuffle Join
。
-
-
调整并行度:
-
通过
spark.default.parallelism
或repartition()
控制分区数。
-
-
数据本地性:
-
确保输入数据与Executor在同一节点(如HDFS副本策略)。
-
-
资源分配:
-
合理设置Executor内存(
spark.executor.memory
)和CPU核心数(spark.executor.cores
)。
-
6. 容错机制
-
Stage重试:若某个Stage失败,重新提交该Stage的所有Task。
-
Task重试:单个Task失败后,TaskScheduler会重新调度(默认最多3次)。
-
血缘恢复:若Executor丢失数据,根据RDD血缘重新计算。
总结
Spark的调度机制通过DAG优化、本地性优先和容错设计,实现了高效的大数据处理。理解其原理后,可通过调整分区策略、优化Shuffle操作等手段显著提升性能。