这是我参与「第四届青训营 」笔记创作活动的第1天
今天复习了Spark Shuffle课程,二刷受益匪浅。
1. Shuffle概述
(1)MapReduce编程模型包含map、shuffle、reduce三个阶段
Map阶段:是在单机上进行的针对一小块数据的计算过程。
Shuffle阶段:在map的基础上进行数据移动,为后续的reduce做准备。
Reduce阶段:对移动后的数据进行处理。
(2)shuffle为什么影响性能?
- 存在M*R次数据移动,网络连接消耗太大
- 可能存在大量的record排序
- java对象在网络中传输还存在大量序列化/反序列化
- 数据压缩
2. Spark Shuffle
(1)宽依赖/窄依赖
窄依赖:父RDD的每个分片至多被子RDD中的一个分片所以来
宽依赖:父RDD中的分片可能被子RDD中的多个分片所以来
当出现宽依赖(ShuffleDependency)Spark就会把运算拆分成两个Stage,即map stage 和 reduce stage产生 Shuffle
(2)Shuffle Dependency-Partitioner
两个接口:numberPartitions 和 getPartition
经典实现:HashPartitioner(先对K取Hashcode,在对分区数取余数)
(3)Shuffle Dependency-Aggregator
createCombiner:只有一个value的时候初始化的方法
mergeValue:合并一个value到Aggregator
mergeCombiners:合并两个Aggregator
3. HashShuffle
在Spark1.2以前,默认使用HashShuffle
每个partition有一个Buffer,直接把partition写到不同的文件中。
最终产生M*R个小文件。
(1) 普通HashShuffle
MapTask会根据ReduceTask的个数将数据分成多个partition(hash % numReduce),同时每个partition会单独溢写到一个独立文件中,最终产生M*R个小文件。
缺点:
- Shuffle阶段在磁盘上产生了过多的小文件,reduce端在拉取数据时会消耗大量IO资源和网络资源。
- 频繁IO,导致读写磁盘时创建对象过多,使堆内存不足,导致OOM。
(2) 优化HashShuffle
开启合并机制(spark.shuffle.consolidateFiles),把每个partition数据映射成为一个文件片段。根据申请到的CPU Core进行写文件,最终会产生C*R个文件。
缺点:
- 如果reduceTask的个数过多仍然会产生过多小文件,造成OOM
4. SortShuffle
(1) SortShuffle
同一个MapTask下的partitions共享buffer,溢写之前进行排序,形成溢写临时文件,最终将所有一些临时文件进行合并生成最终数据文件和索引文件。
(2) bypassMergeShuffle
类似于HashShuffle,但最终还合并所有溢写临时文件生成最终数据文件和索引文件
(3) UnsafeShuffle
使用堆外空间进行溢写,因此成为unsafe;因为在堆外不存在Java对象开销,别不对record进行排序,效率更优。
5. Shuffle优化
Shuffle存在的问题:
- 数据本地存储没有备份
- IO并发,大量RPC请求
- IO吞吐,随机读,写放大
- GC频繁,影响NodeManager
Shuffle优化:
- 1.如果某个RDD数据量很小(1G~2G),那就接使用
broadcast替代join - 2.使用map-side combine,对数据进行预聚合。
- 3.Shuffle参数的优化
Shuffle数据倾斜解决方案:
- 提高并行度,分摊拉取数据量
- Spark 3.0增加AQE功能,自动发现打散倾斜数据。
- Spark运行参数的调参,利用mapside combine
6. Push Shuffle
TODO: