spark头脑镜像

时间:2022-12-12 21:45:47

思考是一件有意思的事情。遇到问题,思考出结论,那么脑子里面的过程是什么呢,或者脑子里面是什么呢。我一直认为,这团团的里面是一个模糊的n维空间。理解一个复杂的系统、公式、算法,都要在这个n维空间里具象化。这个具象化的镜像的精确度就代表了理解的深入度。想起了,考研的时候,太用力,每天晚上脑袋里镜像不断刷新的画面。

最近一直在折腾spark,项目赶得飞快,理解上的问题也一直在积压。今天慢慢梳理,突然发现脑袋里面的镜像构建的不对。

spark的rdd是分布式的存储在内存中的,每个stage的边界是宽依赖导致的shuffle,连续的窄依赖(如map)在一个stage中流水线的执行。之前脑中的镜像是图左,窄依赖不必依赖其他的node,一个node的分区窄依赖变换时不必依赖其他node,所以可以连续执行;宽依赖需要其他node的数据,所以只能shuffle了。

spark头脑镜像

在这种镜像下,我自己很多困惑没有得到解答,比如2个rdd的笛卡尔积为什么是窄依赖呢?笛卡尔积是全连接,需要所有node上的数据。知乎上有个问题是问这个的,有个答案竟然是spark后来的实现打破了自己论文中的设计。有次面试一位多年spark经验的开发,也是这样被误导了。

重新构建一下,如图右。RDD只是一个抽象的概念,分区也是抽象的,并不会存储在内存中,只有调用了cache等明确要求存储的函数时,RDD的分区才会存储到内存变成block。spark运行时并不存在分区,而是对分区进行计算的任务。每个任务其实就是一段执行代码,代码中的内容是就是这一个stage中的连续窄依赖变换,如果图右中的map和groupBy。

那么为什么要把一个job通过宽依赖划分为不同的stage呢?其实很像我们优化一段代码,想提升速度,自然的,开多线程。但如果这段代码中,有一些过程需要依赖前面过程全部执行完才能正确运行。这时,我们切分这段代码为(a,b,c),c依赖b执行完,b依赖a执行完。然后,把这段代码划分为3个stage,每个stage分别多线程运行a,b,c 3块代码。a,b,c内部的操作就是窄依赖,可以并行的执行,a,b,c的边界就是宽依赖,需要前一个stage执行完才可以运行。

这样想,清晰了很多,一个变换是不是窄依赖的决定性因素是:每个分区任务是否可以独立并行的执行。是的话,就可以和其他相邻窄依赖一起合并成任务并行执行。那我们看一下笛卡尔积的例子吧。

spark头脑镜像

如上图,笛卡尔积是可以独立并行执行的,拿到父RDD(2位)的2个分区(m,n)变换出a分区不需要依赖父RDD的其他分区。

笛卡尔积也并有打破论文中的定义:窄依赖是独生家庭,父RDD的一个分区只会唯一生成子RDD的一个分区。假设RDD1(M个分区)和RDD2(N个分区)笛卡尔积得到的子RDD有M*N个分区。父RDD为2个RDD,他的一个分区为(m,n)唯一生成子RDD中的一个分区a,是独生子女,符合政策,不用罚款。

持续构建中。。。

spark头脑镜像

您的关注可能成为我写的动力,助我能每周坚持一篇吧