Spark Application, Job, Stage, Task

本文深入探讨了Apache Spark的执行模型,包括DAG、Jobs、Stages及Tasks的概念及其相互关系。解析了Spark如何通过这些组件高效地管理和执行并行计算任务。

写得很好的一篇文章,学习下。

原文:https://blog.youkuaiyun.com/qq_16146103/article/details/107591802

 

由于 Spark 的懒执行, 在驱动程序调用一个action之前, Spark 应用不会做任何事情.
  针对每个 action, Spark 调度器就创建一个执行图(execution graph)和启动一个 Spark job
  每个 job 由多个stages 组成, 这些 stages 就是实现最终的 RDD 所需的数据转换的步骤. 一个宽依赖划分一个 stage.
  每个 stage 由多个 tasks 来组成, 这些 tasks 就表示每个并行计算, 并且会在多个执行器上执行.
3
注意:Application->Job->Stage-> Task每一层都是1对n的关系。

一. DAG

  DAG(Directed Acyclic Graph)叫做有向无环图,原始的RDD通过一系列的转换就就形成了DAG,根据RDD之间的依赖关系的不同将DAG划分成不同的Stage,对于窄依赖,partition的转换处理在Stage中完成计算。对于宽依赖,由于有Shuffle的存在,只能在parent RDD处理完成后,才能开始接下来的计算,因此宽依赖是划分Stage的依据。
4
  Spark 的顶层调度层使用 RDD 的依赖为每个 job 创建一个由 stages 组成的 DAG(有向无环图). 在 Spark API 中, 这被称作 DAG 调度器(DAG Scheduler).
  我们已经注意到, 有些错误, 比如: 连接集群的错误, 配置参数错误, 启动一个 Spark job 的错误, 这些错误必须处理, 并且都表现为 DAG Scheduler 错误. 这是因为一个 Spark job 的执行是被 DAG 来处理.
  DAG 为每个 job 构建一个 stages 组成的图表, 从而确定运行每个 task 的位置, 然后传递这些信息给 TaskSheduler. TaskSheduler 负责在集群中运行任务.

二. Jobs

  Spark job 处于 Spark 执行层级结构中的最高层. 每个 Spark job 对应一个 action, 每个 action 被 Spark 应用中的驱动所程序调用.
  可以把 Action 理解成把数据从 RDD 的数据带到其他存储系统的组件(通常是带到驱动程序所在的位置或者写到稳定的存储系统中)
  只要一个 action 被调用, Spark 就不会再向这个 job 增加新的东西.

三. stages

  前面说过, RDD 的转换是懒执行的, 直到调用一个 action 才开始执行 RDD 的转换.
  正如前面所提到的, 一个 job 是由调用一个 action 来定义的. 一个 action 可能会包含一个或多个转换( transformation ), Spark 根据宽依赖把 job 分解成 stage.
  从整体来看, 一个 stage 可以任务是“计算(task)”的集合, 这些每个“计算”在各自的 Executor 中进行运算, 而不需要同其他的执行器或者驱动进行网络通讯. 换句话说, 当任何两个 workers 之间开始需要网络通讯的时候, 这时候一个新的 stage 就产生了, 例如: shuffle 的时候.
  这些创建 stage 边界的依赖称为 ShuffleDependencies. shuffle 是由宽依赖所引起的, 比如: sortgroupBy, 因为他们需要在分区中重新分发数据. 那些窄依赖的转换会被分到同一个 stage 中.
  想想我们以前学习的 “worldcount 案例”
5
  Spark 会把 flatMapmap 合并到一个 stage 中, 因为这些转换不需要 shuffle. 所以, 数据只需要传递一次, 每个执行器就可以顺序的执行这些操作.
  因为边界 stage 需要同驱动进行通讯, 所以与 job 有关的 stage 通常必须顺序执行而不能并行执行.
  如果这个 stage 是用来计算不同的 RDDs, 被用来合并成一个下游的转换(比如: join), 也是有可能并行执行的. 但是仅需要计算一个 RDD 的宽依赖转换必须顺序计算.
  所以, 设计程序的时候, 尽量少用 shuffle.

四. Tasks

  stage 由 tasks 组成. 在执行层级中, task 是最小的执行单位. 每一个 task 表现为一个本地计算.
  一个 stage 中的所有 tasks 会对不同的数据执行相同的代码.(程序代码一样, 只是作用在了不同的数据上)
  一个 task 不能被多个执行器来执行, 但是, 每个执行器会动态的分配多个 slots 来执行 tasks, 并且在整个生命周期内会并行的运行多个 task. 每个 stage 的 task 的数量对应着分区的数量, 即每个 Partition 都被分配一个 Task
6
7
  在大多数情况下, 每个 stage 的所有 task 在下一个 stage 开启之前必须全部完成.

<think> 嗯,用户想了解Sparkjobstagetask、宽依赖、窄依赖、行动算子和转换算子的概述。这些概念确实很基础但也很关键,用户可能正在学习Spark的核心原理。 从用户提供的引用材料来看,内容相当全面。引用1解释了stage如何以shuffle为边界划分,以及宽窄依赖的区别;引用2详细列出了Spark的基本概念;引用3提到了driver如何构建DAG和划分stage;引用4说明RDD的五大特性;引用5则补充了窄依赖中OneToOne依赖的特点。这些正好覆盖了用户需要的所有概念。 用户的问题结构很清晰,应该是有备而来。ta可能已经看过官方文档但需要更系统的中文综述,也可能是为面试做准备。考虑到这些术语的关联性,我会用DAG执行流程作为主线串联解释:从行动算子触发jobjob根据shuffle划分stagestage拆分为tasktask执行时根据宽窄依赖决定数据传递方式。 特别要注意几个易混淆点:第一,转换算子延迟执行特性必须强调;第二,宽窄依赖的核心区别在于是否产生shuffle;第三,task才是最终执行单元而非stage。用户提供的引用5中关于OneToOne依赖的描述很精准,可以重点引用。 用户没有要求代码示例,但概念解析需要足够具体。比如解释宽依赖时准备用join操作的例子,窄依赖则用map。引用2中关于executor和driver的定义也要融入说明,这对理解task执行环境很重要。 最后生成的问题要侧重实践:宽窄依赖优化属于高频考点,stage划分原理对调优很关键,算子选择则直接影响性能。用户后续可能会追问调度细节,比如task分配策略或shuffle优化。 </think> 以下是Spark核心概念的清晰概述,结合宽窄依赖、算子类型和执行层级的关系: --- ### **1. Job(作业)** - **定义**:由**行动算子(Action)** 触发的完整计算流程,每个Action对应一个Job[^2][^3]。 - **特点**: - 例如 `collect()`, `count()`, `saveAsTextFile()` 会触发Job执行。 - 一个Application可包含多个Job(每个Action触发一个Job)。 --- ### **2. Stage(阶段)** - **定义**:Job的子集,以 **Shuffle依赖(宽依赖)** 为边界划分[^1][^3]。 - **分类**: - **ShuffleMapStage**:输出数据供后续Stage Shuffle读取。 - **ResultStage**:执行最终计算并输出结果。 - **划分逻辑**: - 从Job的最终RDD反向遍历,遇到宽依赖则切割Stage(窄依赖合并到同一Stage)[^1][^5]。 --- ### **3. Task(任务)** - **定义**:Stage的执行单元,每个分区(Partition)分配一个Task[^2]。 - **类型**: - **ShuffleMapTask**:对应ShuffleMapStage,输出Shuffle数据。 - **ResultTask**:对应ResultStage,输出最终结果。 - **并行度**:由Stage的**分区数**决定(如RDD有100分区 → 生成100个Task)。 --- ### **4. 宽依赖(Wide Dependency)** - **定义**:子RDD的**一个分区依赖父RDD的多个分区**(需Shuffle)[^1][^5]。 - **特点**: - 触发Stage划分(如 `groupByKey()`, `join()`, `repartition()`)。 - 需要跨节点数据传输,性能开销大。 - **示例**: ```plaintext 父RDD分区: [P1, P2] 子RDD分区: [P1'(依赖P1和P2)] // 宽依赖 ``` --- ### **5. 窄依赖(Narrow Dependency)** - **定义**:子RDD的**每个分区只依赖父RDD的一个分区**(无需Shuffle)[^1][^5]。 - **特点**: - 可在同一Stage内管道化执行(如 `map()`, `filter()`)。 - 支持数据本地化计算,高效。 - **示例**: ```plaintext 父RDD分区: [P1, P2] 子RDD分区: [P1'(仅依赖P1), P2'(仅依赖P2)] // 窄依赖 ``` --- ### **6. 转换算子(Transformation)** - **定义**:**延迟计算**的算子,生成新RDD但不立即执行[^4][^5]。 - **常见类型**: - 窄依赖: `map()`, `filter()`, `union()` - 宽依赖: `groupByKey()`, `reduceByKey()`, `join()` - **执行时机**:仅当触发Action时才被调度。 --- ### **7. 行动算子(Action)** - **定义**:触发Job执行的算子,返回非RDD结果(值或输出操作)[^2][^4]。 - **示例**: - `collect()`:返回数据集到Driver。 - `count()`:返回RDD元素总数。 - `saveAsTextFile(path)`:输出数据到存储系统。 --- ### **执行流程总结** ```mermaid graph LR A[Action算子触发Job] --> B{DAG调度器划分Stage} B -->|宽依赖| C[切割Stage] B -->|窄依赖| D[合并到同一Stage] C & D --> E[Task调度器分配Task] E --> F[Executor执行Task] ``` - **关键点**: 1. Action触发Job → 2. DAG根据宽依赖切Stage → 3. Stage转为TaskSet → 4. Task分发到Executor执行[^2][^3][^5]。 --- ### 相关问题 1. **宽依赖如何影响Spark的性能?如何优化Shuffle过程?** 2. **为什么窄依赖比宽依赖执行效率更高?请举例说明。** 3. **DAGScheduler 在Stage划分中具体做了哪些工作?** 4. **如何根据业务逻辑选择转换算子以减少Shuffle?** [^1]: Stages以Shuffle为边界切分,宽依赖需跨Stage [^2]: Job由Action触发,Stage分组TaskTask是执行单元 [^3]: Driver构建DAG并划分Stage [^4]: RDD的依赖关系决定Stage结构 [^5]: 窄依赖分区一对一,宽依赖一对多需Shuffle
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值