Apache Spark 第 4 章:Spark 整体架构
·
作业生命周期
了解 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 完成:
- 划分 Stage:DAGScheduler 遇到 Shuffle(宽依赖) 时会切分 Stage。
- 创建 TaskSet:每个 Stage 根据分区(Partition)数量拆分为一组 Task(统称为 TaskSet)。
- 任务分发: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 的干扰。
最重要的认知是: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 一次作业的真实样子。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)