如何将阶段拆分为Spark中的任务?

时间:2016-05-30 14:25:01

标签: apache-spark

让我们假设以下每个时间点只运行一个Spark作业。

到目前为止我得到了什么

以下是我了解Spark中发生的事情:

  1. 创建SparkContext时,每个工作节点都会启动执行程序。 执行程序是单独的进程(JVM),它连接回驱动程序。每个执行程序都有驱动程序的jar。退出驱动程序,关闭执行程序。每个执行者都可以拥有一些分区。
  2. 执行作业时,将根据沿袭图创建执行计划。
  3. 执行作业分为几个阶段,其中阶段包含尽可能多的邻近(在谱系图中)转换和动作,但没有随机播放。因此,舞台由洗牌分开。
  4. image 1

    我理解

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

    但是

    问题(S)

    如何将舞台拆分为这些任务?

    具体做法是:

    1. 任务是由变换和操作决定的,还是可以在任务中进行多次转换/操作?
    2. 是否由分区确定任务(例如,每个分区每个阶段一个任务)。
    3. 节点是否确定了任务(例如,每个节点每个阶段一个任务)?
    4. 我的想法(只有部分答案,即使是正确的)

      https://0x0fff.com/spark-architecture-shuffle中,随着图像

      解释了随机播放

      enter image description here

      我得到的规则是

        

      每个阶段被分成#count-of-partitions任务,不考虑节点数

      对于我的第一张图片,我说我有3个地图任务和3个减少任务。

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

      无论如何都要打开问题

      这是对的吗?但即使这是正确的,我上面的问题也没有全部回答,因为它仍然是开放的,无论多个操作(例如多个地图)是在一个任务内还是每个操作分成一个任务。

      别人说什么

      What is a task in Spark? How does the Spark worker execute the jar file?How does the Apache Spark scheduler split files into tasks?相似,但我觉得我的问题在那里得不到清楚。

3 个答案:

答案 0 :(得分:37)

这里有一个非常好的大纲。回答你的问题

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

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

我们可以看到以下玩具示例中的行动,其中我们执行以下类型的操作:

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

那么我们最终会有多少个阶段?

  • 每个阶段用于并行加载两个数据源= 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

enter image description here

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

Stage * #Partitions in the stage

的总和

答案 1 :(得分:14)

这可能有助于您更好地理解不同的部分:

  • 阶段:是一组任务。运行相同的进程 不同的数据子集(分区)。
  • 任务:代表一个单位 处理分布式数据集的分区。所以在每个阶段, 任务数=分区数,或者你说“每个任务一个” 每个分区的阶段“。
  • 每个执行器在一个纱线容器上运行,并且 每个容器都驻留在一个节点上。
  • 每个阶段都使用多个执行者,每个执行者都分配了多个vcores。
  • 每个vcore一次只能执行一项任务
  • 因此,在任何阶段,都可以并行执行多个任务。正在运行的任务数=正在使用的正在使用的数量。

答案 2 :(得分:11)

如果我理解正确,有两件事(相关)让你感到困惑:

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 ).

(大多数)更改分区数的操作涉及一个shuffle,例如:

rdd2 = rdd1.repartition( 1000 ) 

实际发生的是rdd1的每个分区上的任务需要产生一个可以被下一阶段读取的结束输出,以使rdd2正好有1000个分区(他们是如何做到的?Hash或{ {3}})。这方面的任务有时被称为“地图(侧面)任务”。 稍后在rdd2上运行的任务将作用于一个分区(rdd2!),并且必须弄清楚如何读取/组合与该分区相关的地图侧输出。这方面的任务有时被称为“减少(侧面)任务”。

这两个问题是相关的:阶段中的任务数是分区数(连续rdds“粘在一起”共同),rdd的分区数可以在阶段之间变化(通过指定数量)分区到一些shuffle引起操作,例如)。

一旦阶段的执行开始,其任务可以占用任务槽。并发任务槽的数量是numExecutors * ExecutorCores。一般来说,这些可以由来自不同非依赖阶段的任务占用。