1

本文以宽依赖和窄依赖的例子来讲解stage切分的过程,这里跟之前不一样的是,join操作是宽依赖
划分到不同的Stage,并构建这些Stage的依赖关系,可以让没有依赖的Stage并行执行,并且让有依赖的Stage顺序执行。并行执行能够有效利用集群资源,提升运行效率,而串行执行则适用于那些在时间和数据资源上存在强制依赖的场景。
为了看的更方便一些,我们把上一篇的最终的图片简化一下,可以看到多了4个灰色的RDD,这是因为有些转换,比如join,里面还有做一些其他的转换操作。
image.png
数据的流程是左边到右边的,stage的切分流程是相反的,是从右边到左边。主要的操作是从右边往左边走,碰到shuffle依赖后,就会把他当做自己的stage的父节点。stage的父节点也会往左边,看看有没有shuffle依赖,重复着上面的过程,直至所有RDD都走完。这样说有些抽象,下面用图形一步步讲解stage切分的全过程。

RDD[11]

首先拿到的是最右边的RDD[11],会把RDD[11]压入waitingForVisit栈中,这个waitingForVisit栈就是存放窄依赖的,然后通过窄依赖的依赖去查找shuffle依赖。此外还有两个数据结构,parents集合,用来存放shuffle依赖,visited集合,用来存放已经遍历过的RDD。
image.png
此时waitingForVisit栈中已经有RDD,就会把RDD[11]拿出来,RDD[11]是没有遍历过的,所以会放入visited集合,另外RDD[11]的依赖RDD[10]是窄依赖,所以RDD[10]就会压入waitingForVisit栈中。
image.png
waitingForVisit栈发现还有RDD,就会把RDD[10]拿出来,RDD[10]是没有遍历过的,所以会放入visited集合,另外RDD[10]的依赖RDD[9]是窄依赖,所以RDD[9]就会压入waitingForVisit栈中。
image.png
waitingForVisit栈发现还有RDD,就会把RDD[9]拿出来,RDD[9]是没有遍历过的,所以会放入visited集合,另外RDD[9]的依赖RDD[4]、RDD[8]是shuffle依赖,所以RDD[4]、RDD[8]会放入parents集合。
image.png
waitingForVisit栈已经为空了,所以就可以开始创建stage。这个stage是右边的,所以还要看看他是否有父节点,也就是是否还有shuffle依赖,这个查找的过程,跟上面一样。

RDD[8]

RDD[11]中的shuffle依赖RDD[8]压入waitingForVisit栈中。
image.png
此时waitingForVisit栈中已经有RDD,就会把RDD[8]拿出来,RDD[8]是没有遍历过的,所以会放入visited集合,另外RDD[8]的依赖RDD[6]、RDD[7]是窄依赖,所以RDD[6]、RDD[7]就会压入waitingForVisit栈中。
image.png
waitingForVisit栈发现还有RDD,就会把RDD[7]拿出来,RDD[7]是没有遍历过的,所以会放入visited集合,另外RDD[7]没有任何依赖,于是不做处理。
image.png
waitingForVisit栈发现还有RDD,就会把RDD[6]拿出来,RDD[6]是没有遍历过的,所以会放入visited集合,另外RDD[6]的依赖RDD[5]是窄依赖,所以RDD[5]就会压入waitingForVisit栈中。
image.png
waitingForVisit栈发现还有RDD,就会把RDD[5]拿出来,RDD[5]是没有遍历过的,所以会放入visited集合,另外RDD[5]没有任何依赖,于是不做处理。
image.png
waitingForVisit栈已经为空了,这次RDD[8]并没有任何shuffle依赖,于是直接创建id为0的stage0。
image.png

RDD[4]

RDD[11]中的shuffle依赖RDD[8]已经处理了,然后shuffle依赖RDD[4]压入waitingForVisit栈中。
image.png
此时waitingForVisit栈中已经有RDD,就会把RDD[4]拿出来,RDD[4]是没有遍历过的,所以会放入visited集合,另外RDD[8]的依赖RDD[3]是窄依赖,所以RDD[3]就会压入waitingForVisit栈中。
image.png
waitingForVisit栈发现还有RDD,就会把RDD[3]拿出来,RDD[3]是没有遍历过的,所以会放入visited集合,另外RDD[3]的依赖RDD[2]是窄依赖,所以RDD[2]就会压入waitingForVisit栈中。
image.png
waitingForVisit栈发现还有RDD,就会把RDD[2]拿出来,RDD[2]是没有遍历过的,所以会放入visited集合,另外RDD[2]的依赖RDD[1]、RDD[0]是shuffle依赖,所以RDD[1]、RDD[0]会放入parents集合。
image.png
waitingForVisit栈已经为空了,所以又开始创建stage了。
由于RDD[1]、RDD[0]没有父节点,于是就直接id为1、2的stage1和stage2。
image.png
然后我们回到RDD[4],为RDD[4]创建stage,由于他shuffle依赖为stage1和stage2,所以RDD[4]创建了一个id为3,父节点为stage1和stage2的stage3。
image.png

RDD[11]

我们再回到RDD[11],此时他的shuffle依赖RDD[4]和RDD[8]都已经创建好了stage,所以RDD[11]创建了一个id为4,父节点为stage3和stage0的stage4。至此,stage创建完成。
stage4是ResultStage,对RDD中的分区进行计算并得出最终结果,所以主要进行作业的收尾工作,比如把数据写入各种目标源。
而stage0到stage3是ShuffleMapStage,属于DAG调度流程的中间Stage。
image.png


大军
847 声望183 粉丝

学而不思则罔,思而不学则殆