Spark Shuffle

shuffle-洗牌,从字面意义来说,shuffle就是将原本的数据基于另一种规则重新排序。
Spark在DAG阶段以Shuffle为界,划分stage,前一个stage称为为map task,后一个称为Reduce task。Map Task将计算结果分成多份,每一份对应到Reduce Task的每个partition中,并将其临时写到磁盘,此过程为Shuffle Write,而Reduce Task通过网络拉取Map Task 的指定分区结果数据,此过程称为Shuffle Read。从上述介绍可以看出,Shuffle过程涉及到了网络IO、写磁盘等耗时的操作,所以整个Shuffle阶段是极其昂贵的,Spark在Shuffle的实现做了许多优化。SparkShuffle分为两种,一种是基于Hash的Shuffle,一种是基于Sort的shuffle。
Spark Shuffle大致过程

HashShuffleManager

Hash算法:又称散列算法。只要符合散列思想的算法都称之为Hash算法。
Spark 最开始默认的使用的是Hash Based Shuffler,Mapper会根据Reducer数量创建相应的Bucket数量,这样会导致Bucket数量为Mapper数量与Reducer数量的乘积,从而产生许多小文件,这样会产生大量的I/O,从而给文件系统造成很大的压力,此外在数据传输过程中频繁的网络通信,会增加网络通信出现故障的可能性,从而导致task失败,诱发spark重试机制,严重制约Spark性能。
优化:可以通过将spark.shuffle.consolidateFiles参数设置为true,使map端的一个Core上的Map Task的通过Hash算法得到的相同key到一个文件中,从而达到减少文件数量的目的。中间产生的文件数为Core数量*Reducer数量。
Spark Hash优化后的流程

SortShuffleManager

Hash算法的Shuffle过程中间小文件的个数都依赖于ReduceTask的个数,在海量数据的处理过程中,这种产生的小文件的个数不可控。为了解决这一问题,Spark在后期版中中引入了SortShuffleManager。
SortShuffleManager有三种机制,以不同的ShuffleWrite过程,分出来三种机制,分别为普通运行机制(SortShuffleWriter)、Bypass运行机制(BypassMergeSortShuffleWriter)、Tungsten SortShuffle运行机制(UnsafeShuffleWriter)。

普通运行机制

  1. Map Task的结果会先写入到一个内存数据结构中,该数据结构的选择与spark算子有关,如果是像reduceByKey这样的聚合类的算子,则延用Map的数据结构;如果是join这类型的算子,则会采用Array数据结构。写入的内存数据结构默认是5M。
  2. 在Shuffle写入磁盘前会预估文件的大小,用于申请内存
  3. 如果申请成功,则不会进行溢写;如果申请不成功,则会发生溢写到磁盘,然后清空内存数据结构,再写入之前会使用PartitionAppendOnlyMap/PartitionedPairBuffer在内存中对Key进行排序。
  4. 溢写是以batch的形式去写入的,默认一个batch的数量为1万条。写入磁盘文件是通过Java的BufferOutputStream实现的,这样会将数据存在内存中,当内存满了再一次写入到磁盘中,减少了磁盘I/O次数,提升了性能。
  5. Map Task执行完成后,会将这些磁盘小文件Merge合并成一个大文件,同时生成一个索引文件,索引文件中标识了Reduce Task的Start Offset和End Offset。一个Task只对应一个磁盘文件,Reduce Task根据索引标识,读取对应的磁盘文件。
  6. Reduce Task再拉取Map端数据之前会先解析索引文件,再根据索引拉取数据。
    注:如果需要数据全局有序的文件,则会进行一次全局排序。
    SortShuffleManager大致过程
    产生文件数量为2*MapTask数量,这样很大程度上减少了小文件的数量。

Bypass运行机制

实现的是带Hash风格的基于Sort的Shuffle机制,为每个Reduce端的任务构建一个输出文件。在Reducer端任务数较少时,基于Hash的Shuffle实现机制明显比基于Sort的Shuffle实现机制要快。
采用此机制的条件:

  1. 不指定Ordering,即不需要对分区内部排序,减少了这部分的开销
  2. Reducer Task分区个数小于“spark.shuffle.sort.bypassMergeThreshold”配置属性个数(默认200个)
  3. 在map端没有聚合操作(像groupByKey,手动实现map端没有聚合CombinByKey等)。

bypass机制与普通机制的区别是1.磁盘写机制不同;2.在shuffle write过程中不需要进行聚合及排序操作。

TungstenSortShuffle运行机制

从Spark1.5.0开始,Spark开始了Tungsten项目,目的是优化内存和CPU的使用,进一步提升Spark性能,使用了内存管理器和二级制格式的序列化器。Unsafe Shuffle的做法是将数据记录用二进制的方式存储在堆外内存中,不需要数据反序列化而进行高效的排序和传输。
Spark默认开启的是基于SortShuffle实现机制,基于Tungsten Sort的Shuffle入口仍然是SortShuffleManger类,其实现机制使用的ShuffleHandle与ShuffleWriter分别为SerializedShuffleHandle与UnsafeShuffleWriter。
运用TungstenSortShuffle运行机制的参数是spark.shuffle.manager=tungsten-sort,但是这个设置并不能保证一定采用TungstenSort的shuffle实现机制。
使用UnsafeShuffleWriter的条件比较苛刻,需要满足以下条件:

  1. 不支持Combine或者排序操作(UnsafeShuffleWriter会对数据进行序列化,数据存储为字节,字节不进行排序);
  2. Shuffle的序列化器是KryoSerializer或者SparkSQL自定义的序列化方式;
  3. Shuffle文件数量不能大于16777216(2的24次方)
  4. 序列化时,单条记录不能大于128M。

小结

通过SortShuffleWriter.shouldBypassMergeSort方法判断是否需要回退到Hash类型的Shuffle实现机制,不满足时,通过SortShuffleManager.canUseSerialzedShuffle方法判断是否需要采用基于TungstenSortShuffle实现机制,当二者都不满足时,会采用普通运行机制。

官网的参数配置页:
https://spark.apache.org/docs/latest/configuration.html

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

难得将就

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值