Spark 核心之 Stage 和 Task 原理剖析

简介: Spark中,Job由Action触发,DAGScheduler依Shuffle依赖切分为Stage:前序为ShuffleMapStage(生成中间文件),末尾为ResultStage(产出结果);每个Stage按分区数生成对应ShuffleMapTask或ResultTask。本文深度解析Stage划分、Task生成与序列化机制。(239字)

摘要:如果说 Job 是 Spark 的"任务单",Stage 就是"施工阶段",Task 就是每个工人的"具体活"。一个 Job 被 DAGScheduler 沿 Shuffle 边界切分为多个 Stage——前面的全是 ShuffleMapStage,最后一个必须是 ResultStage。每个 Stage 的 Partition 数决定了 Task 数量,ShuffleMapStage 产生 ShuffleMapTask(写 Shuffle 文件),ResultStage 产生 ResultTask(直接返回结果)。本文从 Stage 类型体系、DAG → Stage 切分源码、Task 生成与序列化、两种 Task 执行差异四个维度,配合 1 张原创深色架构图 + 完整源码分析,带你彻底看懂 Spark 最核心的执行引擎。

关键词:Spark Stage, ShuffleMapStage, ResultStage, ShuffleMapTask, ResultTask, DAGScheduler, Task 序列化, MapOutputTracker


一、开篇:Stage 和 Task 是什么关系?

先说结论:

Job = 用户的一个 Action 操作
  ├── Stage 0: ShuffleMapStage → 2 个 ShuffleMapTask
  └── Stage 1: ResultStage     → 3 个 ResultTask
概念 定义 数量
Stage Shuffle 边界切分的计算阶段 每个 Job 可有多个
Task 处理一个 Partition 的最小计算单元 每个 Stage 可有多个
ShuffleMapStage 输出 Shuffle 中间文件的 Stage Job 中除最后一个外的所有
ResultStage 输出最终结果的 Stage 每个 Job 有且仅有一个

二、Stage 与 Task 全景图

在这里插入图片描述

三、Stage 切分:从 RDD DAG 到 Stage

3.1 核心源码

// 源码:DAGScheduler.scala - 创建 ResultStage
private def createResultStage(finalRDD: RDD[_], func: (TaskContext, Iterator[_]) => _,
    partitions: Array[Int], jobId: Int, callSite: CallSite): ResultStage = {
  // 从 finalRDD 回溯 → 遇到 ShuffleDep → 创建 ShuffleMapStage
  val parents = getOrCreateParentStages(finalRDD, jobId)
  val id = nextStageId.getAndIncrement()
  new ResultStage(id, finalRDD, func, partitions, parents, jobId, callSite)
}

// 递归获取父 Stage
private def getOrCreateParentStages(rdd: RDD[_], firstJobId: Int): List[Stage] = {
  rdd.dependencies.flatMap {
    case shufDep: ShuffleDependency[_, _, _] =>
      getOrCreateShuffleMapStage(shufDep, firstJobId) :: Nil
    case _ => Nil  // NarrowDep 不切分
  }.toList
}

3.2 Stage 提交顺序

// 递归提交:先父后子
private def submitStage(stage: Stage): Unit = {
  val missing = getMissingParentStages(stage).sortBy(_.id)
  if (missing.isEmpty) {
    submitMissingTasks(stage, jobId.get)  // 无缺失父 Stage → 执行
  } else {
    for (parent <- missing) submitStage(parent)  // 递归提交父 Stage
  }
}

四、Task 生成:从 Stage 到 TaskSet

// 源码:DAGScheduler.scala - submitMissingTasks()
private def submitMissingTasks(stage: Stage, jobId: Int): Unit = {
  // 计算需要计算的 Partition(跳过已完成的)
  val partitionsToCompute = stage.findMissingPartitions()

  // 为每个 Partition 创建一个 Task
  val tasks: Seq[Task[_]] = stage match {
    case stage: ShuffleMapStage =>
      partitionsToCompute.map { id =>
        new ShuffleMapTask(stage.id, stage.rdd, stage.shuffleDep, ...)
      }
    case stage: ResultStage =>
      partitionsToCompute.map { id =>
        new ResultTask(stage.id, stage.rdd, stage.func, id, ...)
      }
  }

  // 封装为 TaskSet,提交给 TaskScheduler
  taskScheduler.submitTasks(new TaskSet(tasks.toArray, stage.id, ...))
}

Task 数量 = Stage 最后一个 RDD 的 Partition 数量。


五、两种 Stage 与两种 Task 对比

5.1 ShuffleMapStage + ShuffleMapTask

// ShuffleMapTask.runTask() — 执行逻辑
override def runTask(context: TaskContext): MapStatus = {
  val writer = new ShuffleWriter(partition, shuffleDep)
  // ① 执行 RDD 算子链(map/flatMap/filter...)
  val iter = rdd.iterator(partition, context)
  // ② 将结果写入 Shuffle 文件
  writer.write(iter)
  // ③ 返回 MapStatus(文件位置 + 分区长度)
  writer.stop(success = true).get
}

5.2 ResultStage + ResultTask

// ResultTask.runTask() — 执行逻辑
override def runTask(context: TaskContext): U = {
  // ① 执行 RDD 算子链
  val iter = rdd.iterator(partition, context)
  // ② 将最终结果应用 func(如 collect 的收集逻辑)
  func(context, iter)
  // ③ 序列化结果 → StatusUpdate → Driver
}

5.3 对比表

维度 ShuffleMapStage ResultStage
Task 类型 ShuffleMapTask ResultTask
输出 Shuffle 中间文件 最终计算结果
返回类型 MapStatus U (泛型)
一个 Job 中的数量 0~N 1(唯一)

六、Task 序列化

# 推荐 Kryo 序列化(比 Java 快 10 倍)
--conf spark.serializer=org.apache.spark.serializer.KryoSerializer
--conf spark.kryo.registrationRequired=true  # 强制注册
// 代码中注册 Kryo 类
val conf = new SparkConf()
  .set("spark.serializer", "org.apache.spark.serializer.KryoSerializer")
  .registerKryoClasses(Array(classOf[MyDataClass], classOf[MyModel]))

为什么需要序列化? Driver 端的 Task 对象(包含 RDD 算子闭包)需要跨网络发送到 Executor,必须序列化为字节流。


七、总结

要点 总结
Stage 切分 遇到 ShuffleDependency 即切分,递归提交(先父后子)
Task 生成 每个 Partition → 一个 Task,类型由 Stage 决定
两种 Stage ShuffleMapStage(写 Shuffle) + ResultStage(返回结果)
序列化 Task 闭包必须可序列化,推荐 Kryo

金句:Stage 是 Spark 的"流水线工位",Task 是每个工位上的"工人"。Shuffle 就是工位之间的传送带——上一个工位写完,下一个工位才能开始。



作者:starzy | AI Data Engineer / 大数据技术实践者

博客:blog.starzy.cn | GitHub:starzy1990.github.io

专注 AI Agent · LangGraph · RAG · 大数据架构 · 数据工程实践

目录
相关文章
|
25天前
|
缓存 边缘计算 监控
阿里云 CDN 内容分发网络服务租用价格表(一年 / 按月 / 按小时报价明细)
阿里云CDN通过就近分发缩短内容传输距离,显著改善访问速度。计费上以按量后付费为弹性补充、资源包预付费为长期优惠,流量与带宽两种计费模式按业务特征选择。HTTPS请求数、动态请求数等增值资源按需选购。做预算测算时,将流量/带宽费用与HTTPS、动态请求费用一并计入,监控用量、优化缓存命中率,在保障访问体验的同时有效控制CDN成本。
251 1
|
25天前
|
弹性计算 运维 安全
阿里云数字证书管理服务(原SSL证书)租用价格表(一年 / 按月 / 按小时报价明细)
阿里云数字证书管理服务,覆盖从个人站点到大型企业的HTTPS加密需求,证书主体以1/2/3年预付费订阅为主,托管、域名风险巡检增值服务支持按月订阅。选型核心是根据业务场景选择DV/OV/EV证书,区分单域名、通配符、多域名。预算核算时,除证书本身订阅费用,还要评估是否需要托管、风险巡检、部署次数包等增值服务。借助CAS CLI可以实现证书批量管理、自动化巡检,提前预警到期证书,保障网站HTTPS长期稳定安全。
164 0
|
23天前
|
人工智能
Codex 新增2个非常好用的命令,真香
Codex CLI 新增 `/copy`(一键复制AI完整回复,告别鼠标拖选)和 `/clear`(双模清屏:保留上下文或开启新对话),解决终端复制易断、清屏丢历史等高频痛点,打磨交互细节,让AI编程更顺滑。
130 0
|
23天前
|
Rust IDE 开发工具
Zed 新特性:这次抄了IDEA,抄的很好
Zed新版本上线「选中即搜索」功能:终端或编辑器中选中文件名/路径,按快捷键唤起文件查找,搜索框自动填充选中文本,一键跳转,告别复制粘贴。对标JetBrains体验,细节处见真章。(238字)
117 0
|
25天前
|
人工智能 算法 程序员
以前创业的壁垒是你会不会做,2026年的壁垒是有没有人用你——2个大学生把训练视频做成App,半年赚50万
本文是「OPC一人公司通关手册」第37篇,拆解两个不会写代码的大学生如何用AI无代码工具,2周打造田径跑姿分析App:上传视频→AI逐帧诊断→给出改进建议。凭15万田径圈粉丝“预销售”,上线首月入7万,半年总收入约50万元,4万用户,4.7分好评。核心启示:受众先于产品、内容即冷启动、定价即产品、长尾SEO胜爆款词。(239字)
|
25天前
|
弹性计算 运维 小程序
阿里云轻量应用云服务器租用价格表(一年/按月/按小时报价明细)
阿里云轻量应用服务器 适合个人建站、测试项目、小程序后端和中小型业务,价格最低、运维最省心;如果业务需要稳定独享带宽或高并发承载,建议升级到ECS标准实例。省钱的核心不是单纯比价,而是让配置与业务负载匹配,再叠加新用户权益、优惠券、大促节点和年付折扣。
285 0
|
25天前
|
监控 IDE 安全
阿里云 Qoder CN 服务订阅租用价格表(一年/按月/按小时报价明细)
个人日常开发优先Pro套餐;经常做大型工程重构、代码批量迁移,选择Pro+。长期持续开发直接选择年付订阅,拿到折扣降低年均成本。团队根据实际开发人数采购席位,不要过量采购。企业私有化部署场景,提前评估50席位的最低采购门槛,合理规划预算。日常开发定期查看Credits消耗,监控编码智能体任务资源使用,及时采购资源包,避免任务中途中断。
218 0
|
19天前
|
人工智能 JSON 自然语言处理
LangChain 六大使用场景:哪些值得做,哪些不值得,一张决策图说清
本文系统梳理LangChain六大核心场景(RAG、Agent、对话系统、结构化提取、内容生成、多模态),逐项解析痛点、机制、落地要点与适用边界,并附“不该用”的反向清单。强调“模型+外部世界”协同才是框架价值所在,助力AI工程化理性选型。
359 0
|
15天前
|
人工智能 中间件 Java
LangChain 到底是什么:从链式连接到 Agent 运行时,讲透 v1.0 的核心概念
LangChain 是大模型应用开发框架,非单纯API封装。v1.0聚焦Agent工程化:以`create_agent`为核心,基于LangGraph实现可持久化、可中断、可观测的智能体循环;统一模型接口、强化工具描述、分层记忆管理,并通过中间件支持人工介入、摘要压缩与隐私脱敏,推动AI从“能跑”走向“可靠生产”。(239字)
271 1
|
19天前
|
SQL 存储 分布式计算
一篇文章搞懂 Hive:从数仓定位到架构、表设计与数据倾斜治理
Hive是基于Hadoop的SQL数据仓库工具,不存数据、不算数据,仅作“胶水层”:元数据存MySQL,数据落HDFS,计算交YARN。支持内外表、分区/分桶、ORC/Parquet列存及丰富优化策略,专为海量离线分析而生,是企业级数仓十五年事实标准。(239字)
572 1

热门文章

最新文章