作业生命周期

了解 Spark 作业的生命周期是进行性能调优和排查问题的核心。一个程序从执行 spark-submit 开始,到任务结束,主要经历 提交、逻辑构建、物理规划、执行 四个阶段。
以下是详细的生命周期拆解:

1. 提交阶段 (Submission)

当你运行 spark-submit 命令时:

  • 启动 Driver 进程:系统根据部署模式(Client 或 Cluster)启动 Driver。
  • 资源申请:Driver 向 Cluster Manager(如 YARN, Kubernetes, Standalone)申请运行 Executor 的资源。
  • Executor 启动:资源管理器在各个计算节点上启动 Executor 进程,并向 Driver 注册。

2. 逻辑规划阶段 (Logical Planning)

Driver 运行用户的代码,这是“翻译”的开始:

  • 构建 RDD 依赖图 (DAG):Spark 解析代码,记录算子之间的依赖关系(宽依赖或窄依赖),形成 DAG(有向无环图)。
  • Spark SQL 优化 (Catalyst):如果是 SQL/DataFrame:
  • Unresolved Logical Plan:检查表名和列名。
    • Logical Plan:应用优化规则(如谓词下推、列裁剪)。

3. 物理调度阶段 (Physical Scheduling)

这是最关键的拆解步骤,由 DAGScheduler 和 TaskScheduler 完成:

  1. 划分 Stage:DAGScheduler 遇到 Shuffle(宽依赖) 时会切分 Stage。
  2. 创建 TaskSet:每个 Stage 根据分区(Partition)数量拆分为一组 Task(统称为 TaskSet)。
  3. 任务分发:TaskScheduler 将 Task 发送到有空闲资源的 Executor 上。它会遵循数据本地性(Data Locality)原则,尽量让计算去靠近数据。

4. 执行阶段 (Execution)

Executor 接收到 Task 后:

  • 反序列化:Executor 下载 Task 相关的代码和依赖。
  • 内存申请:Task 开始运行,向 MemoryManager 申请 Execution Memory(包括堆内或你之前提到的堆外内存)。
  • 计算与 Shuffle:
  • 如果是 Map Stage,结果可能写入本地磁盘(Shuffle Write)。
    • 如果是 Reduce Stage,会跨网络拉取数据(Shuffle Read)。

5. 结束阶段 (Completion)

  • 结果返回:Task 执行结果返回给 Driver(如 collect)或直接写入外部存储(如 saveAsTextFile)。
  • 注销资源:所有 Task 完成后,Driver 向资源管理器申请注销 Executor,最后 Driver 进程自身退出。

核心组件链路图

User Code -> DAG -> Stages -> Tasks -> Executors

关键点提示

  • 惰性求值 (Lazy Evaluation):在遇到 Action 算子(如 count, save)之前,Spark 不会真正触发上述周期,只会记录逻辑。
  • 堆外内存的作用点:在“执行阶段”,如果开启了堆外内存,Shuffle 的中间结果序列化数据会直接存储在操作系统的内存中,减少 JVM GC 的干扰。
5. 销毁 (Cleanup)
4. 执行与存储 (Execution)
3. 物理调度 (Scheduling)
2. 逻辑构建 (Logical Plan)
1. 提交阶段 (Submission)
1. 启动
2. 注册并申请资源
3. 分配容器
3. 分配容器
4. 解析代码

No

Yes

5. DAGScheduler

根据 Shuffle 边界

6. TaskScheduler
7. 分发任务
7. 分发任务
8. 计算
8. 计算

数据拉取

9. 最终聚合

spark-submit

Driver 进程

Cluster Manager: YARN/K8s

Executor 1

Executor 2

Action 算子触发?

记录 RDD 血缘/逻辑计划

构建 DAG 有向无环图

划分 Stages

Stage 0 / Stage 1...

拆解为 TaskSets

Shuffle Write / Result

Shuffle Read

写入 HDFS/数据库

Driver 关闭

释放所有 Executor 资源

最重要的认知是:Driver 始终是大脑,Executor 只是无脑的执行者,它们完全听从 Driver 的调度。

在这里插入图片描述

Driver 内部结构

Driver 内部结构(第二张) 拆解了"大脑"的各个部件。

  • DAG Scheduler 负责把 RDD 血统转成 Stage 依赖关系,是逻辑层。
  • Task Scheduler 负责把 Stage 拆成 Task 并通过 Scheduler Backend 发给 Executor,是物理层。
  • SQL Engine / Catalyst 专门处理 DataFrame 和 SQL 的查询优化,在 Task 生成之前就把执行计划优化到最佳。
  • Broadcast Manager 把小表一次性广播到所有 Executor 的内存,后续 join 时直接本地查询,避免 Shuffle。
  • Memory Manager 和 Block Manager 则负责整个作业期间的内存分配和数据块追踪。
    在这里插入图片描述

Executor 内存模型

Executor 内存模型(第三张) 是性能调优的地基,必须搞懂。

8GB 的 Executor 内存被划为三块:

  • 300MB 系统保留;
  • 剩余的 60%(约 4.6GB)是 统一内存区,由执行内存和存储内存动态共享,执行内存用于 Shuffle/Sort/Join,存储内存用于 cache() 和广播变量;
  • 剩余的 40%(约 3.1GB)是用户内存,给你自己写的 UDF、RDD 算子里的 Python/Java 对象用,Spark 不管理这块,写出内存泄漏最容易在这里崩。
  • 另外堆外内存(Off-Heap)是完全独立的,不受 JVM GC 管理,大状态计算时开启可以显著减少 GC 停顿。
    在这里插入图片描述

三种部署模式

三种部署模式(第四张) 的本质差异在于"Driver 在哪、谁管资源"。

  • Local 模式一切在一个 JVM 进程里,没有网络开销,只用来本地开发。
  • Standalone 是 Spark 自带的轻量调度器,适合快速搭建小集群。
  • YARN/K8s 是企业生产环境的标配,YARN 与 Hadoop 生态深度整合,K8s 则更适合云原生环境。

注意: YARN/K8s 还有 client 和 cluster 两种提交模式的区别——cluster 模式下 Driver 运行在集群内部,提交机断开没关系,生产一律用 cluster 模式。
在这里插入图片描述

全景流向图

全景流向图(第五张) 把所有组件拼在一起。

  • 数据流方向:存储层(S3/HDFS)→ Executor 并行读入各自的 Partition → Task 线程处理 → Shuffle 数据在 Executor 间网络传输 → 最终结果回传 Driver。
  • 调度流方向:Driver Task Scheduler → 通过虚线分发 Task 给三个 Executor → Executor 执行后结果沿橙色虚线返回。

两个流向交叉运行,就是 Spark 一次作业的真实样子。
在这里插入图片描述

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐