Spark Shuffle
目录
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过程中不会进行排序操作。 |
本文详细介绍了Spark中ShuffleManager的两种实现方式:HashShuffleManager和SortShuffleManager,包括它们的历史演变、工作机制、优化策略以及相关配置参数。深入探讨了Shuffle阶段划分、byPass机制和调优建议。

被折叠的 条评论
为什么被折叠?



