一.什么是 Spark Shuffle
- ReduceByKey 的含义?
ReduceByKey 会将上一个 RDD 中的每一个 key 对应的所有 value 聚合成一个 value,然后生成一个新的 RDD,元素类型是<key,value>对的形式,这样每一个 key 对应一个聚合起来的 value。
- 问题:每一个 key 对应的 value 不一定都是在一个 partition 中,也不可能在同一个节点上,因为 RDD 是分布式的弹性数据集,它的 partition 极有可能分布在各个节点上。
- 如何聚合?
- Shuffle Write:上一个 stage 的每个 map task 就必须保证将自己处理的当前分区中的数相同的 key 写入一个分区文件中,可能会写入多个不同的分区文件中。
- Shuffle Read:reduce task 就会从上一个 stage 的所有 task 所在的机器上寻找属于自己的那些分区文件,就可以保证每一个 key 对应的 value 都会汇聚到同一个节点上去聚合和处理。
- 针对上图中的 Shuffle 过程可能会产生的问题?
- 小文件过多,耗时低效的 IO 操作
- OOM,读写文件以及缓存过多
二.HashShuffle
上图中,每个 Buffer缓冲区 大小为32k ,个数与 reducetask 个数相同,每个 Buffer 缓冲区溢出写到一个落地磁盘小文件,并且要求每个 Buffer 缓冲区溢出写完成后再聚合(即 Shuffle 完成后再聚合),每个 reduce task 再去所有磁盘小文件中拉去属于自己分区的那个小文件。
以上方式( Hash Shuffle 普通机制)产生的磁盘小文件数 block file num= map task num* reduce task num
缺点:
- 写磁盘小文件的对象多
- Shuffle read 的时候节点之间的连接对象多
- 大量占用 Executor 端的内存,容易OOM,导致 JVM 不停地进行 GC
- 因为可能跨节点进行磁盘小文件拉取,节点之间的连接创建地会比较多,就有可能因为网络卡顿造成的连接断掉
将这种方式进行优化:
每个 Executor 中一个 core 中运行的的 task 会共用一份 Buffer 缓冲区 。
因此,产生的磁盘小文件数 block file num=core num*reduce task num。
优化后至少减少一半的磁盘小文件。
三.SortShuffle
- SortShuffle 的运行机制主要分为两种:
- 普通运行机制
- bypass运行机制
- SortShuffle 两种运行机制的区别?
普通运行机制:
- task 首先将数据写入一个内存数据结构中,这个内存数据结构初识大小为5M
- 存在一个不定期估算内存机制,它根据你往该内存数据结构中写入数据的速度预测你下一次写入之后内存数据结构会有多大,譬如,当下一次写入之后内存数据结构大小预测为5.01M时,会申请估算值两倍的内存减去当前内存得到实际申请内存值,再加到原内存中,即——5.01M*2-5M+5M=10.02M,最终内存大小为10.02M,依次类推
- 当内存数据结构大小达到阈值后,会进行溢写,溢写的过程会有排序,随后分批写入磁盘小文件,最终得到一个索引文件和一个磁盘文件
- reduce task 查数据的时候,先去查索引文件,再去查磁盘文件
- 它产生的磁盘小文件个数为2*map task num
bypass运行机制:
与普通机制相比,少了排序部分
当我们的业务不需要排序,并且 reduce task 大于触发条件默认值200时,可以通过手动调大该参数的值,比如调到1001。