Spark Shuffle
目录
Spark Shuffle
Shuffle Version
Shuffle阶段划分
Hash Based Shuffle Manager
未经优化的hashShuffleManager
优化后的Hash Shuffle
Sort Based Shuffle Manager
SortShuffle的普通机制
byPass机制
Shuffle Version
Spark1.1以前的版本一直是采用Hash Shuffle的实现的方式,
1.1版本时参考Hadoop MapReduce的实现开始引入Sort Shuffle,
1.5版本时开始Tungsten钨丝计划,引入UnSafe Shuffle优化内存及CPU的使用,
1.6中将Tungsten统一到Sort Shuffle中,实现自我感知选择最佳Shuffle方式,
2.0版本,Hash Shuffle已被删除,所有Shuffle方式全部统一到Sort Shuffle一个实现中。
Spark中负责shuffle过程的执行、计算和处理的组件主要就是ShuffleManager,也即shuffle管理器。ShuffleManager随着Spark的发展有两种实现的方式,分别为HashShuffleManager和SortShuffleManager,因此spark的Shuffle有Hash Shuffle和Sort Shuffle两种。
Shuffle阶段划分
shuffle write:mapper阶段,上一个stage得到最后的结果写出
shuffle read :reduce阶段,下一个stage拉取上一个stage进行合并
Hash Based Shuffle Manager
(Spark1.1之前)
HashShuffle是根据task的计算结果的 key值的hashcode%ReduceTask 来决定放入哪一个分区,这样保证相同的数据一定放入一个分区
未经优化的hashShuffleManager
上游State的RDD的每一个分区对应每一个线程, 都会产生与下游相同分区数量的文件
优化后的Hash Shuffle
Shuffle write过程中,上游state的每一个RDD的每一个分区对应一个线程,不再产生 与下游state等分区数量的文件。
而是,由executor来输出与下游的RDD分区数等量的文件。(一个Executor中有多个task)
Sort Based Shuffle Manager
sort shuffle存在两种运行的机制: 普通机制 和 bypass机制
SortShuffle的普通机制
数据会先写入一个内存数据结构中(默认5M),此时根据不同的shuffle算子,可能选用不同的数据结构。如果是reduceByKey这种聚合类的shuffle算子,那么会选用Map数据结构,一边通过Map进行聚合,一边写入内存;如果是join这种普通的shuffle算子,那么会选用Array数据结构,直接写入内存。
接着,每写一条数据进入内存数据结构之后,就会判断一下,是否达到了某个临界阈值。如果达到临界阈值的话,那么就会尝试将内存数据结构中的数据溢写到磁盘,然后清空内存数据结构
溢写的过程中,会对数据进行分区排序,排序过后,会分批将数据写入磁盘文件。默认的batch数量是10000条
一个task将所有数据写入内存数据结构的过程中,会发生多次磁盘溢写操作,也就会产生多个临时文件。最后会将之前所有的临时磁盘文件都进行合并成1个磁盘文件
一个task就只对应一个磁盘文件,也就意味着该task为Reduce端的stage的task准备的数据都在这一个文件中,因此还会单独写一份索引文件,其中标识了下游各个task的数据在文件中的start offset与end offset。
byPass机制
bypass运行机制的触发条件如下:
- 第一 ,shuffle map task数量小于spark.shuffle.sort.bypassMergeThreshold=200参数的值;
第二,不是map combine聚合的shuffle算子(比如reduceByKey有map combie)
task会为每个reduce端的task都创建一个临时磁盘文件,并将数据按key进行hash,然后根据key的hash值,将key 溢写 到对应磁盘文件
该过程的磁盘写机制其实跟未经优化的HashShuffleManager一样,因为都要创建数量惊人的磁盘文件,只是在最后会做一个磁盘文件的合并而已。因此少量的最终磁盘文件,也让该机制相对未经优化的HashShuffleManager来说,shuffle read的性能更好。
与SortShuffle普通机制的区别在于:
第一,磁盘写机制不同;
第二,不会进行排序。也就是说,启用该机制的最大好处在于,shuffle write过程中,不需要进行数据的排序操作
相关参数
参数 | 参数说明 |
spark.shuffle.file.buffer | 该参数用于设置shuffle write task的BufferedOutputStream的buffer缓冲大小(默认是32K)。将数据写到磁盘文件之前,会先写入buffer缓冲中,待缓冲写满之后,才会溢写到磁盘。 |
spark.reducer.maxSizeInFlight | 该参数用于设置shuffle read task的buffer缓冲大小,而这个buffer缓冲决定了每次能够拉取多少数据。(默认48M) |
spark.shuffle.io.maxRetries | shuffle read task从shuffle write task所在节点拉取属于自己的数据时,如果因为网络异常导致拉取失败,是会自动进行重试的。该参数就代表了可以重试的最大次数。(默认是3次) |
spark.shuffle.io.retryWait | 该参数代表了每次重试拉取数据的等待间隔。(默认为5s) 调优建议:一般的调优都是将重试次数调高,不调整时间间隔。 |
spark.shuffle.memoryFraction | 该参数代表了Executor内存中,分配给shuffle read task进行聚合操作内存比例。 |
spark.shuffle.manager | 该参数用于设置shufflemanager的类型(默认为sort) Hash:spark1.x版本的默认值,HashShuffleManager Sort:spark2.x版本的默认值,普通机制,当shuffle read task 的数量小于等于spark.shuffle.sort.bypassMergeThreshold参数,自动开启bypass 机制 |
spark.shuffle.sort.bypassMergeThreshold | 当ShuffleManager为SortShuffleManager时,如果shuffle map task的数量小于这个阈值(默认是200),则shuffle write过程中不会进行排序操作。 |