Shuffle操作的原理与源码分析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Smart_cxr/article/details/81202719

普通的shuffle操作

第一个特点,

    在Spark早期版本中,那个bucket缓存是非常非常重要的,因为需要将一个ShuffleMapTask所有的数据都写入内存缓存之后,才会刷新到磁盘。但是这就有一个问题,如果map side数据过多,那么很容易造成内存溢出。所以spark在新版本中,优化了,默认那个内存缓存是100kb,然后呢,写入一点数据达到了刷新到磁盘的阈值之后,就会将数据一点一点地刷新到磁盘。

    这种操作的优点,是不容易发生内存溢出。缺点在于,如果内存缓存过小的话,那么可能发生过多的磁盘写io操作。所以,这里的内存缓存大小,是可以根据实际的业务情况进行优化的。

第二个特点,

    与MapReduce完全不一样的是,MapReduce它必须将所有的数据都写入本地磁盘文件以后,才能启动reduce操作,来拉取数据。为什么?因为mapreduce要实现默认的根据key的排序!所以要排序,肯定得写完所有数据,才能排序,然后reduce来拉取。

    但是Spark不需要,spark默认情况下,是不会对数据进行排序的。因此ShuffleMapTask每写入一点数据,ResultTask就可以拉取一点数据,然后在本地执行我们定义的聚合函数和算子,进行计算。

    spark这种机制的好处在于,速度比mapreduce快多了。但是也有一个问题,mapreduce提供的reduce,是可以处理每个key对应的value上的,很方便。但是spark中,由于这种实时拉取的机制,因此提供不了,直接处理key对应的values的算子,只能通过groupByKey,先shuffle,有一个MapPartitionsRDD,然后用map算子,来处理每个key对应的values。就没有mapreduce的计算模型那么方便。

优化后的Shuffle操作

接下来是对源码进行分析

这是将每个ShuffleMapTask计算处理的新的RDD的partition数据写入本地磁盘也就是写操作(Write)在HashShuffleWriter类中

进入forMapTask()方法

接下来是read操作,ResultTask或者ShuffleMapTask,在执行到ShuffledRdd时,肯定会调用ShuffledRDD的compute()方法,来计算当前这个RDD的partition的数据,这个在Task原理源码剖析的时候已经分析过了。

进入HashShuffleReader类中的read()方法

进入BlockStoresShuffleFetcher中的fetch()方法

①拿到了一个全局的MapOutputTrackerMaster的引用,然后调用起方法getServerStatuses()传入了shuffleId和reduceId,shuffleId代表了当前这个stage的上一个stage,我们知道shuffle是分为两个stage的,shuffle write是发生在上一个stage中,shuffle read是发生在当前的stage中的,也就是通过shuffleId可用限制到上一个stage的所有shuffleMapTask的输出的MapStatus,接着通过reduceid,也就是bucketId,来限制从每个MapStatus中,获取当前这个ResultTask需要获取的每个ShuffleMapTask的输出文件的信息,这个getServerStatuses()方法一定是走远程网络通信的,因为要联系Driver上的DAGScheduler的MapOutputTrackerMaster

进入getServerStatuses()方法

Shuffle的读写操作的源码分析就到这里了

猜你喜欢

转载自blog.csdn.net/Smart_cxr/article/details/81202719