Spark中RDD的宽窄依赖 & 图解RDD执行中Application、Job、Stage、Task的关系

目录

1. RDD之间的依赖(以分区为说明)

2. RDD任务划分原理

3. Spark中RDD执行阶段划分示意图


1. RDD之间的依赖(以分区为说明)

窄依赖:每一个父RDD的Partition中的数据,最多被子RDD的一个Partition使用(单分区 -> 单分区);

              窄依赖在源码里是OneToOneDependency

宽依赖:同一个父RDD的Partition中的数据,被多个子RDD的Partition使用(单分区 -> 多分区),会引起shuffle,数据打乱重组;典型的宽依赖是ByKey的各种算子,依据key进行suffle。

画一张简单的示意图来看宽窄依赖的区别:

2. RDD任务划分原理

窄依赖不会shuffle,所有的RDD分区转换可以并行进行,所以各种task可以在同一个stage中进行;

宽依赖由于会产生shuffle,上一个stage没完,数据不会进行shuffle到下一个stage,下一个stage只能等待,所以宽依赖是划分阶段的依据

RDD任务切分级别为:Application、Job、Stage、Task

(1)Application:初始化一个SparkContext即生成一个Application;

(2)Job:一个action算子生成一个Job;

(3)Stage:遇到一个宽依赖划分一个Stage;

(4)Task:一个分区对应一个task,将Stage划分的结果发送到不同的Executor中执行。

关系:

1个Application中至少有1个Job;

1个Job中依据宽窄依赖划分至少有一个Stage(至少有一个ResultStage);

1个Stage中依据分区至少有一个Task(至少一个分区)。

ps:shuffle之后会产生新的Stage,重新分区

3. Spark中RDD执行阶段划分示意图

通过示意图,分析Spark中任务如何划分,阶段如何划分,依赖关系是怎样的,以及最后如何执行的。

eg:跑一个wordCount的小demo,Spark阶段划分如下

(1)首先通过sc.textfile从文件中读取数据到Partition0和1中;

(2)然后通过flatMap算子进行扁平化,分区不改变;

(3)做统计转换,用map算子,A -> (A,1)变成了元组的形式,分区依然没变;

(4)reduceByKey,相同的key聚合在一起,分区数据被打乱shuffle重组;

(5)collect

整个过程是 窄依赖 -> shuffle -> 宽依赖

调用行动算子的时候,会运行作业(JOB),进行阶段(Stage)的划分以及提交阶段(SubmitStage)后的任务(Task)划分,最后提交任务(submitTask)。

(1)划分阶段(查找shuffle)

提交作业首先划分阶段,首先一定会有一个ResultStage;

然后再看之前有没有其他阶段,根据血缘关系从最后一个RDD往前看,查找宽依赖,即shuffleDependency;

创建ShuffleMapStage,与shuffle后的RDD区别开,

这样阶段划分完毕,形成2个阶段:ShuffleMapStage -------shuffle-----------ResultStage

(2)提交ShuffleMapStage阶段,划分任务

提交ResultStage阶段前,会找上一级阶段,也就是ShuffleMapStage;

接着看ShuffleMapStage有没有上一级阶段,发现没有,开始对ShuffleMapStage阶段划分task;

task数取决于分区数,这里分了2个区,所以划分了2个Task,并行计算,提交给Executor执行;

(3)shuffle等待

shuffle等待上一个阶段出结果,源码中waitChildStage;

(4)提交ResultStage阶段

再从ResultStage开始依次提交各个阶段。

所以总流程是:

划分Stage(从后往前查找shuffle)-> 划分任务Task -> 提交阶段(从前往后提交,shuffle等待)

猜你喜欢

转载自blog.csdn.net/wx1528159409/article/details/87636135