这是我参与「第四届青训营 」笔记创作活动的第9天
本次笔记重点内容
- shuffle概述
- spark中的shuffle算子
- spark中的shuffle过程
- push shuffle社区的实现方案
Shuffle操作
在spark框架中,shuffle是支撑spark进行大规模复杂数据处理的基石
MapReduce
第一步分布式处理叫Map,第二步分成单份数据处理叫Reduce,中间移动数据(在Map的基础上)的部分就是Shuffle,被分成一块一块的数据叫Partition。
为什么Shuffle对性能非常重要?
- 巨量的网络连接,大量的数据移动
- 数据在移动和计算过程中存在丢失风险,存在大量数据序列化和反序列化操作——把某一内存中直观的数据转换成二进制数字流存到文件中,再从文件中读取到内存中来重新变成一个对象,消耗大量CPU
- 可能存在大量排序操作
Shuffle算子
常见的触发shuffle的算子
repartition改变数据的分区,ByKey把Key聚合到一起(比如相同颜色的数据聚合在一起),Distinct是特殊的ByKey操作,join把本身不是一起的数据按某种条件聚到一起
Shuffle Dependency
创建会产生shuffle的RDD时,RDD会创建Shuffle Dependency来描述Shuffle相关的信息,Shuffle Dependency构造函数里面包含若干变量。
- A single key-value pair RDD
- Partitioner 给定一个K并产生其对应分区(映射成数字),有numberPartitions(一共多少个分区)和getPartition(取K告诉对应的partition)两个接口
- Serializer 把对象和二进制数据流互相映射
- Optional key ordering——K排序
- Optional Aggregator 在map侧合并部分record的函数,有createCombiner(只有一个value的时候初始化)、mergeValue(初始化之后,如果再来一个value,就merge到Aggregator中去,形成一个新的Aggregator)和mergeCombiners(合并两个Aggregator)三种方法
- mapSideCombine flag which is disabled (i.e. false) by default.
Shuffle过程具体实现
- 写数据 HashShuffle,每个partition会映射到一个独立文件,这样就会导致生成的文件过多,运行过程中同时打开的文件也很多,所以改进为映射到一个文件片段;SortShuffle,每个Task生成一个包含所有partition数据的文件
- 读数据 每个reduce task分别获取所有map task生成属于自己的片段
Register Shuffle
Register Shuffle根据不同条件创建不同Shuffle Handle
不同的Shuffle Handle会对应不同的Shuffle Writer
- BypassMergeSortShuffleWriter,不排序节省CPU,不同partition能直接到不同文件里面,只适应于partition较少的情况
- UnsafeShuffleWriter,unsafe是因为用了对外的内存,对内有个Long Array记录原信息
- SortShuffleWriter,支持combine
ShuffleReader网络请求时序图
使用netty作为网络通信框架提供网络服务
ShuffleBlockFetchIterator--Reader实现
区分local和remote节省网络消耗,防止OOM
External Shuffle Service--Read实现
每个Executor会去自身节点的ESS那注册,让ESS了解每个本地任务产生的Shuffle数据位置,每个reduce任务找到对应任务的ESS来连接数据。
Shuffle优化技术——Netty Zero Copy
DMA
是一个接口,外部设备不用通过CPU就可直接与系统内存交换数据
- 堆外内存,可以避免对内对外互相的拷贝
- Netty中有些对象,这些对象有些方法,可以合并、包装、切分数组,避免发生内存拷贝,在没有必要copy数据的时候通过包装的方式达到原目的
- 使用 FileRegion 实现文件传输,可以将文件缓冲区的数据直接传输到目标 Channel,避免了还要去应用程序里读文件再写入数据流的数据拷贝
怎么优化?
- RDD对应的数据量较小时,可以避免shuffle,使用broadcast替代join
- 使用可以map-side预聚合的算子,减少shuffle时的数据量
- shuffle参数优化,根据作业不同的表现调整参数(并发度、partition数量、读取合并数据、SparkSQL中、AQE参数等)
- shuffle倾斜优化
每个Task处理量不同,处理时间差异大,端到端时间变长
提高并行度,AQE检测倾斜数据,将倾斜的分区打散,然后各自进行join
Push Shuffle
解决大量随机读的问题,严重浪费CPU资源,那么能不能在读取Shuffle数据之前就合并数据?
Magnet
在ESS基础上增加了一个merge功能,很多个Map Task写完自己的本地文件之后,会发一个push请求给Magnet,Magnet接收之后就把这些文件merge到一起,形成一个统一的file,对于没有来得及完成合并或者合并失败的情况,则fallback到原模式。