要真正理解 Spark 的运行机制,首先必须掌握其分布式架构中的关键角色。一个 Spark 应用程序 (Application) 的成功运行,离不开 Driver、Cluster Manager、Worker 和 Executor 等角色的协同工作。本节,我们将深入剖析这些核心角色职责相互关系

一、Spark 应用程序的核心角色

一个完整的 Spark Application 主要由以下几个核心角色构成:

a. Driver (驱动程序)

核心职责: 应用程序的“大脑”。它是整个应用入口总指挥
识别标志: 哪里创建了 SparkContext (或 SparkSession),哪里就是 Driver。
具体工作:

  1. 运行应用程序的 main 函数
  2. 创建 SparkContext,这是与集群交互的唯一通道
  3. 向 Cluster Manager (如 Standalone Master, YARN ResourceManager) 申请计算资源 (Executor)
  4. 对作业 (Job) 进行分析,并将其划分多个阶段 (Stage)
  5. 每个阶段任务 (Task) 分发到各个 Executor 上执行
  6. 收集任务的执行结果

在这里插入图片描述

b. Cluster Manager (集群管理器)

核心职责: 集群的“资源管家”。负责管理和分配集群中的物理资源
不同模式下的实现:

Standalone 模式: 角色是 Master 节点
YARN 模式: 角色是 ResourceManager
Kubernetes 模式: 角色是 Kubernetes Master (API Server)

在这里插入图片描述

c. Worker Node (工作节点)

核心职责: 集群中的“劳动力”。任何可以运行 Application 计算代码物理或虚拟机器。
具体工作:

  1. 接收 Cluster Manager 的指令
  2. 在其上启动和管理一个或多个 Executor 进程。

在这里插入图片描述

d. Executor (执行器)

核心职责: 任务的“执行单元”。它是一个运行在 Worker Node 上的 JVM 进程 (或可视为一个线程池)。
具体工作:

  1. 接收 Driver 分发过来的任务 (Task)
  2. 在自己的线程中执行这些 Task,进行具体的计算
  3. 计算结果返回给 Driver,或者写入外部存储 (如 HDFS)
  4. 缓存 RDD 分区数据

e. Task (任务)

核心职责: 工作的“最小单位”。一个 Task 是一个被发送到 Executor 的工作单元,通常在一个线程中执行。
具体工作: 对一个 RDD一个分区 (Partition) 执行一系列计算操作 (如 map, filter)。

在这里插入图片描述

二、RDD 分区与 Task 的并行执行

理解 Spark 如何实现并行计算,关键在于理解 RDD 分Task 之间的关系

RDD (弹性分布式数据集):是 Spark 中最基本数据抽象。一个 RDD 逻辑上是一个完整的数据集,但物理上切分成了多个分区分布在集群的不同节点上。

分区与 HDFS Block 的关系:当我们从 HDFS 读取文件创建 RDD 时,通常情况下,HDFS 中的一个 Block对应 RDD 的一个 Partition。例如,一个 300MB 的文件在 HDFS 中被存储为 128MB, 128MB, 44MB 三个 Block,那么通过 sc.textFile() 读取后,生成的 RDD 很可能也会有 3 个分区。

并行计算的核心:Spark 为 RDD 的每一个分区启动一个 Task 来进行计算。如果有 N 个分区,Spark 就可以并行地运行 N 个 Task,从而实现分布式并行计算

在这里插入图片描述

代码执行流程解析:
当我们编写并提交如下代码时:

# ... SparkContext 已创建
textRDD = sc.textFile("hdfs://...")
flatmap_RDD = textRDD.flatMap(lambda line: line.split(" "))
mapRDD = flatmap_RDD.map(lambda word:(word,1))
resultRDD = mapRDD.reduceByKey(lambda a,b:a+b)
resultRDD.collect()

sc.textFile(...): 读取数据并创建 RDD,确定分区数。
flatMap, map, reduceByKey: 这些是转换 (Transformation) 操作,它们并不会立即执行,只是记录了 RDD 之间的依赖关系,形成一个有向无环图 (DAG)
collect(): 这是一个行动 (Action) 操作。它的触发,会让 Driver 将整个 DAG 提交给集群执行。Driver 将计算任务分解为 Task,分发给 Executor。Executor 启动多个 Task 线程,每个线程负责处理一个分区上的数据并依次执行 flatMap, map, reduceByKey一系列计算。

三、Driver 端与 Executor 端的代码执行划分

在一个 Spark Application 中,并非所有代码都在Executor 上执行。理解代码的执行位置对于调试和性能优化至关重要。

在这里插入图片描述

执行位置划分:

在 Driver 端执行的代码

  1. 创建 SparkContext/SparkSession: sc = SparkContext(...)
  2. 所有 RDD 转换操作的“定义”: 当你写 rdd.map(...)rdd.filter(...) 时,这些代码本身是在 Driver 端构建 DAG 的一部分,并不是在 Executor 上立即执行
  3. 触发 Action 的代码: rdd.collect(), rdd.count() 等。Action 本身是在 Driver 端调用的,它触发了在 Executor 上的计算
  4. 处理 Action 结果的代码: collect() 返回的结果列表是在 Driver 端的内存中的。所有对这个结果列表的操作 (如 for 循环打印) 都在 Driver 端执行。
  5. 关闭 SparkContext: sc.stop()

在 Executor 端执行的代码

  1. RDD 转换操作的“函数体”: 在 rdd.map(lambda x: x + 1) 中,lambda x: x + 1 这个函数本身会被序列化发送到 Executor,并由 Task 线程在数据分区实际执行
  2. 所有数据处理逻辑: 包括从数据源 (如 HDFS) 读取数据中间转换计算、Shuffle 数据的读写等,都是在 Executor 中完成的。

四、spark-shell, pyspark, spark-submit 的关系与参数

spark-shell (Scala) 和 pyspark (Python) 是交互式的 Shell 环境,非常适合探索性分析快速原型开发。而 spark-submit 是用于提交已打包好的独立应用 (JARs or Python scripts) 到集群通用工具

核心关系:

spark-shellpyspark底层实际上也是调用 spark-submit启动一个 Spark 应用的。
因此,所有可以传递spark-submit配置参数同样可以启动 spark-shellpyspark使用

常用 spark-submit 参数解析 (YARN 模式下)
下表总结了一些在 YARN 模式下提交任务时最常用的参数:

参数 (Parameter) 作用 默认值 示例
--master 指定集群管理器 local[*] yarn
--deploy-mode 部署模式 (clientcluster)。 client cluster
--driver-memory Driver进程的内存。 1g 2g
--driver-cores Driver进程的CPU核心数 (仅限cluster模式)。 1 2
--num-executors 启动的Executor数量。 2 10
--executor-memory 每个Executor的内存。 1g 4g
--executor-cores 每个Executor的CPU核心数。 1 4

示例命令:提交一个配置丰富的 Spark 作业到 YARN

spark-submit \
--master yarn \
--deploy-mode cluster \
--driver-memory 1g \
--driver-cores 1 \
--num-executors 10 \
--executor-memory 2g \
--executor-cores 2 \
--name "MyComplexApp" \
my_spark_app.py arg1 arg2

资源计算示例解析:
根据上面的命令,我们向 YARN 集群申请了多少资源?
Driver 资源: 1g 内存, 1 个 CPU core (运行在某个NodeManager上)。
Executor 总资源:

  • 总Executor内存: 10 (executors) * 2g (memory/executor) = 20g
  • 总Executor核心数: 10 (executors) * 2 (cores/executor) = 20 cores

整个应用总共需要 1g + 20g = 21g 内存和 1 + 20 = 21 个CPU核心。在提交前,你需要确认 YARN 集群有足够可用资源</-s>。

在这里插入图片描述

日期:2025年10月6日
专栏:Spark教程(PySpark)

Logo

助力广东及东莞地区开发者,代码托管、在线学习与竞赛、技术交流与分享、资源共享、职业发展,成为松山湖开发者首选的工作与学习平台

更多推荐