如何在Spark中将阶段划分为任务?


143

接下来,我们假设每个时间点仅运行一个Spark作业。

我到目前为止所得到的

这是我了解Spark会发生的情况:

  1. SparkContext被创建,每个工作节点开始执行人。执行程序是单独的进程(JVM),它们连接回驱动程序。每个执行程序都有驱动程序的jar。退出驱动程序,关闭执行程序。每个执行程序可以容纳一些分区。
  2. 执行作业时,根据沿袭图创建执行计划。
  3. 执行作业分为多个阶段,其中的阶段包含(在沿袭图中)尽可能多的相邻转换和动作,但没有混洗。因此,各个阶段被随机播放分开。

图片1

我明白那个

  • 任务是通过序列化Function对象从驱动程序发送给执行程序的命令。
  • 执行程序反序列化(使用驱动程序jar)命令(任务)并在分区上执行。

问题

我如何将阶段划分为这些任务?

特别:

  1. 任务是由转换和动作确定的还是一个任务中可以有多个转换/动作?
  2. 任务是否由分区确定(例如,每个分区每个阶段每个任务一个)。
  3. 任务是否由节点确定(例如,每个节点每个阶段一个任务)?

我的想法(即使是正确的,也只能部分回答)

https://0x0fff.com/spark-architecture-shuffle中,随机播放与图片一起说明

在此处输入图片说明

我觉得规则是

每个阶段都分为#number-of-partitions个任务,不考虑节点数量

对于我的第一张图片,我会说我要执行3个贴图任务和3个缩小任务。

对于来自0x0fff的图像,我想说有8个地图任务和3个缩小任务(假设只有三个橙色和三个深绿色文件)。

在任何情况下都可以提问

那是对的吗?但是即使那是正确的,我的上述问题也没有全部回答,因为它仍然是开放的,是一项任务中包含多个操作(例如,多个地图)还是一项操作被分为一个任务。

别人怎么说

Spark中的任务是什么?Spark worker如何执行jar文件?如何在Apache星火调度分割文件转换成任务?相似,但是我不觉得我的问题在那儿得到了清晰的回答。

Answers:


52

您在这里有一个非常漂亮的轮廓。回答您的问题

  • task 确实需要为每个分区的每个数据分区启动一个单独的分区stage。考虑到每个分区可能驻留在不同的物理位置上-例如,HDFS中的块或本地文件系统的目录/卷。

请注意,Stages 的提交受的驱动DAG Scheduler。这意味着可以将不相互依赖的阶段提交给集群以并行执行:这可以最大化集群上的并行化能力。因此,如果数据流中的操作可以同时发生,那么我们将期望看到多个阶段启动。

我们可以在下面的玩具示例中看到这一点,其中我们执行以下类型的操作:

  • 加载两个数据源
  • 在两个数据源上分别执行一些映射操作
  • 加入他们
  • 对结果执行一些映射和过滤操作
  • 保存结果

那么,我们最终将经历多少个阶段?

  • 每个阶段需要1个阶段,以并行方式加载两个数据源= 2个阶段
  • 表示第三阶段join依赖于其他两个阶段
  • 注意:对联接数据进行的所有后续操作都可能在同一阶段执行,因为它们必须顺序发生。启动其他阶段没有任何好处,因为它们无法完成之前的操作才能开始工作。

这是玩具程序

val sfi  = sc.textFile("/data/blah/input").map{ x => val xi = x.toInt; (xi,xi*xi) }
val sp = sc.parallelize{ (0 until 1000).map{ x => (x,x * x+1) }}
val spj = sfi.join(sp)
val sm = spj.mapPartitions{ iter => iter.map{ case (k,(v1,v2)) => (k, v1+v2) }}
val sf = sm.filter{ case (k,v) => v % 10 == 0 }
sf.saveAsTextFile("/data/blah/out")

这是结果的DAG

在此处输入图片说明

现在:有多少个任务?任务数应等于

Stage* #Partitions in the stage)之和


2
谢谢!请就您的案文详细说明您的答案:1)我对阶段的定义是否不全面?听起来我错过了阶段不能包含可以并行进行的操作的要求。还是我的描述已经严格暗示了这一点?2)必须为作业执行的任务数由分区数决定,而不是由处理器或节点的数目决定,而可以同时执行的任务数则取决于分区数。处理器,对不对?3)一个任务可以包含多个操作?
Make42

1
4)您的最后一句话是什么意思?毕竟,数字分区在每个阶段都可能有所不同。您是说这就是您在所有阶段配置工作的方式吗?
Make42 '16

@ Make42当然分区的数量可以随阶段而变化-您是正确的。我的意图是说sum(..)要考虑到这种差异。
javadba

哇,您的回答完全可以,但是很遗憾,最后一句话肯定是错误的概念。这并不意味着一个阶段中的分区数量等于处理器数量,但是,您可以根据计算机上显示的内核数量来设置RDD的分区数量。
epcpu

@epcpu这是一个特例-但我同意这会产生误导,因此我将其删除。
javadba

26

这可以帮助您更好地理解不同的部分:

  • 阶段:是任务的集合。针对不同数据子集(分区)运行相同的进程。
  • 任务:代表分布式数据集分区上的工作单元。因此,在每个阶段,任务数=分区数,或者如您所说的“每个分区每个阶段一个任务”。
  • 每个执行程序在一个纱线容器上运行,并且每个容器驻留在一个节点上。
  • 每个阶段利用多个执行程序,每个执行程序分配有多个vcore。
  • 每个vcore一次只能执行一个任务
  • 因此,在任何阶段都可以并行执行多个任务。正在运行的任务数=正在使用的内核数。

2
这是关于Spark
pedram bashiri

我没有得到您的要点编号3。据我所知,每个节点可以有多个执行程序,所以根据要点3:每个节点应该只有一个执行程序。您能澄清这一点吗?
Rituparno Behera

@RituparnoBehera每个节点可以具有多个容器,因此可以有多个Spark执行程序。查看此链接。docs.cloudera.com/runtime/7.0.2/running-spark-applications/...
pedram bashiri

15

如果我正确理解,有2件(相关的)事情会使您感到困惑:

1)什么决定了任务的内容?

2)什么决定了要执行的任务数量?

Spark的引擎将连续rdds上的简单操作“粘合”在一起,例如:

rdd1 = sc.textFile( ... )
rdd2 = rdd1.filter( ... )
rdd3 = rdd2.map( ... )
rdd3RowCount = rdd3.count

因此,(懒散地)计算rdd3时,spark将为rdd1的每个分区生成一个任务,并且每个任务将按行执行过滤器和映射,以生成rdd3。

任务数由分区数决定。每个RDD具有定义数量的分区。对于从HDFS读取的源RDD(例如,使用sc.textFile(...)),分区数是由输入格式生成的拆分数。对RDD进行的某些操作可能会导致RDD具有不同数量的分区:

rdd2 = rdd1.repartition( 1000 ) will result in rdd2 having 1000 partitions ( regardless of how many partitions rdd1 had ).

另一个例子是联接:

rdd3 = rdd1.join( rdd2  , numPartitions = 1000 ) will result in rdd3 having 1000 partitions ( regardless of partitions number of rdd1 and rdd2 ).

(大多数)更改分区数量的操作涉及洗牌,例如,当我们这样做时:

rdd2 = rdd1.repartition( 1000 ) 

实际发生的是rdd1的每个分区上的任务需要产生一个最终输出,该输出可以在随后的阶段读取,从而使rdd2恰好具有1000个分区(它们是怎么做的?HashSort)。这边的任务有时称为“地图(边)任务”。稍后将在rdd2上运行的任务将作用于rdd2!的一个分区上,并且必须弄清楚如何读取/组合与该分区相关的地图端输出。这边的任务有时称为“减少(边)任务”。

这两个问题是相关的:一个阶段中的任务数是分区的数目(对于连续的rdds“粘合”在一起是共有的),并且rdd的分区数可以在各个阶段之间改变(通过指定某些分区的数目)造成洗牌)。

一旦开始执行阶段,其任务就可以占用任务槽。并发任务插槽的数量为numExecutors * ExecutorCores。通常,这些可以被来自不同,非依赖阶段的任务占用。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.