这是我参与「第四届青训营」笔记创作活动的的第6天
01.Shuffle概述
1.1 MapReduce概述
▪️ 2004年,谷歌发布了《MapReduce:Simplified Data Processing on Large Clusters》论文
▪️ 在开源实现的MapReduce中,存在Map、Shuffle、Reduce三个阶段。
1.1 Map阶段
Map阶段,是在单机上进行的针对一小块数据的计算过程
1.2 Shuffle阶段
Shuffle 阶段,在map阶段的基础上,进行数据移动,为后续的reduce阶段做准备。
1.3 Reduce过程
reduce阶段,对移动后的数据进行处理,依然是在单机上处理一小份数据
1.4 为什么shuffle对性能非常重要
▪️ M * R次网络连接
▪️ 大量的数据移动
▪️ 数据丢失风险
▪️ 可能存在大量的排序操作
▪️ 大量的数据序列化、反序列化操作
▪️ 数据压缩
总结
在大数据场景下,数据shuffle表示了不同分区数据交换的过程,不同的shuffle策略性能差异较大。目前在各个引擎中shuffle都是优化的重点,在spark框架中,shuffle 是支撑spark进行大规模复杂数据处理的基石。
02.Shuffle算子
2.1 Shuffle算子分类
Spark中会产生shuffle的算子大概分为4类
2.1 Shuffle 算子
Spark源码中RDD的单元测试
Spark PairRDDFunctions的单位测试
val text = sc. textFile("mytextfile. txt")
val counts = text
.flatMap(line => line.split(" ")) .map(word => (word,1)) .reduceByKey(_+_)counts. collect
2.2 Spark中对shuffle的抽象-宽依赖、窄依赖
● 窄依赖:父RDD的每个分片至多被子RDD中的一个分片所依赖
● 宽依赖:父RDD中的分片可能被子RDD中的多个分片所依赖
2.2算子内部的依赖关系
2.2.1 Shuffle Dependency 构造
1)A single key-value pair RDD, i.e. RDD[Product2[K, V]],
2)Partitioner (available as partitioner property),
3)Serializer,
4)Optional key ordering (of Scala's scala.math.Ordering type),
5)Optional Aggregator,
6)mapSideCombine flag which is disabled (i.e. false) by default.
2.2.1 Shuffle Dependency 构造 - Partitioner
abstract class Partitioner extends Serialiable {
def numPartitions: Int
def getPartition(key: Any): Int }
class HashPartitioner (partitions: Int) extends Partitioner {
require(partitions >= 0, s"Number of
def numPartitions: Int = partitions
def getPartition(key: Any): Int = key
cace nu11 -> 0 case _ -> Utils.nonNegativeMod(key.hasCode,numPartitions)}
}
▪️ 两个接口
1)numberPartitions 2)getPartition
▪️ 经典实现
HashPartitioner
2.2.1 Shuffle Dependency构造 - Aggregator
▪️ createCombiner: 只有一个value的时候初始化的方法
▪️ mergeValue: 合并一个value 到 Aggregator
▪️ mergeCombiners: 合并两个Aggregator
03.Shuffle过程
03.Shuffle实现的发展历程
▪️ Spark 0.8 及以前 Hash Based Shuffle
▪️ Spark 0.8.1 为 Hash Based Shuffle引入 File Consolidationt机制
▪️ Spark 0.9 引入 ExternalAppendOnlyMap
▪️ Spark 1.1 引入 Sort Based Shuffle, 但默认为 Hash Based Shuffle
▪️ Spark 1.2 默认的 Shuffle 方式改为 Sort Based Shuffle
▪️ Spark 1.4 引入 Tungsten-Sort Based Shuffle
▪️ Spark 1.6 Tungsten-Sort Based Shuffle 并入 Sort Based Shuffle
▪️ Spark 2.0 Hash Based Shuffle 退出历史舞台
3.1 Hash Shuffle - 写数据
每个partition会映射到一个独立的文件
3.1 Hash Shuffle - 写数据优化
每个partition会映射到一个文件片段
3.2 Sort shuffle:写数据
每个task生成一个包含所有partiton数据的文件
3.3 Shuffle - 读数据
每个reduce task分别获取所有map task生成的属于自己的片段
3.4 Shuffle过程的触发流程
val text = sc. textFile("mytextfile. txt") val counts = text
.flatMap(line => line.split(" ")) .map(word => (word,1)) .reduceByKey(_+_ .)counts. collect
3.5 Shuffle Handle的创建
Register Shuffle时做的最重要的事情是根据不同条件创建不同的shuffle Handle
3.6 Shuffle Handle与Shuffle Writer的对应关系
3.7 Writer FII - BypassMergeShuffle Writer
● 不需要排序,节省时间
● 写操作的时候会打开大量文件
● 类似于Hash Shuffle
● 使用类似内存页储存序列化数据
● 数据写入后不再反序列化
3.7 Writer实现- UnsafeShuffle Writer
● 只根据partition 排序Long Array
● 数据不移动
3.7 Writer FIЛ - SortShuffle Writer
▪️ 支持 combine
▪️ 需要combine时, 使用PartitionedAppendOnlyMap, 本质是个HashTable
▪️ 不需要combine时,PartitionedPairBuffer本质是个array
3.8 Reader实现 - 网络时序图
▪️ 使用基于netty的网络通信框架
▪️ 位置信息记录在MapOutputTracker中
▪️ 主要会发送两种类型的请求
1)OpenBlocks请求 2)Chunk请求或Stream请求
3.8 Reader 实现 - ShuffleBlockFetchIterator
1)区分local和remote节省网络消耗
2)防止 OOM
▪️ maxBytesInFlight
▪️ maxReqsInFlight
▪️ maxBlocksInFlightPer Address
▪️ maxReqSizeShuffleToMem
▪️ maxAttemptsOnNettyOOM
3.8 Read 实现 - External Shuffle Service
ESS作为一个存在于每个节点上的agent为所有Shuffle Reader提供服务,从而 优化了Spark作业的资源利用率,MapTask在运行结束后可以正常退出。
3.9 Shuffle优化使用的技术Zero Copy
3.9 Shuffle优化使用的技术: Netty Zero Copy
1)可堆外内存,避免JVM堆内存到堆外内存的数据拷贝。
2)CompositeByteBuf 、Unpooled. wrappedBuffer、ByteBuf.slice ,可以合并、包装、切分数组,避免发生内存拷贝
3)Netty 使用FileRegion实现文件传输,FileRegion 底层封装了FileChannel#transferTo() 方法,可以将文件缓冲区的数据直接传输到目标Channel,避免内核缓冲区和用户态缓冲区之间的数据拷贝
3.10 常见问题
➢数据存储在本地磁盘,没有备份
➢IO并发:大量RPC请求(M*R)
➢IO吞吐:随机读、写放大(3X)
➢GC频繁,影响NodeManager
3.11 Shuffle 优化
▪️ 避免shuffle
▪️ 使用broadcast替代join
3.11 Shuffle优化
使用可以map-side预聚合的算子
3.12 Shuffle 参数优化
▪️ spark. default. parallelism && spark. sql. shuffle. partitions
▪️ spark. hadoopRDD. ignoreEmptySplits
▪️ spark. hadoop. mapreduce. input. fileinputformat. split.minsize
▪️ spark. sql. file. maxPartition Bytes
▪️ spark. sql. adaptive. enabled && spark. sql. adaptive. shuffle. targetPostShuffleInputSize
▪️ spark. reducer. maxSizeInFlight
▪️ spark. reducer. maxReqsInFlight
spark. reducer . maxBlocksInFlightPerAddress
3.13 Shuffle倾斜优化
● 什么叫shuffle倾斜
● 倾斜影响
1)作业运行时间变长
2)Task OOM导致作业失败
3.13常见的倾斜处理办法
● 提高并行度
● 优点:足够简单
● 缺点:只缓解、不根治
3.13 Spark AQE Skew Join
AQE根据shuffle文件统计数据自动检测倾斜数据,将那些倾斜的分区打散成小的子分区,然后各自进行join。
3.14案例-参数优化
▪️ ad_ show
1)number of files read: 840,042
2)number of total tasks: 5,553
3)size of files read: 203.3 TiB
4)number of output rows: 128,676,054, 598
3.15 参数调整
▪️ spark.sql.adaptive.shuffle. targetPostShuffleInputSize :
▪️ spark.sql.files.maxPartitionBytes :
04. Push Shuffle
4.1为什么需要Push Shuffle ?
▪️ Avg IO size太小,造成了大量的随机IO,严重影响磁盘的吞吐
▪️ M*R次读请求,造成大量的网络连接,影响稳定性
4.2 Push Shuffle 的实现
Facebook : cosco
Linkedln: magnet
Uber: Zeus
Alibaba:RSS
Tencent:FireStorm
Bytedance:CSS
Spark3.2:push based shuffle
4.3 Magnet实现原理
1)Spark driver组件,协调整体的shuffle操作
2)map任务的shuffle writer过程完成后,增加了一个额外的操作push-merge,将数据复制一份推到远程shuffle服务上
3)magnet shuffle service是一个强化版的ESS。将隶属于同一个shuffle partition的block,会在远程传输到magnet后被merge到一个文件中
4)reduce任务从magnet shuffle service接收合并好的shuffle数据
4.3 Magnet实现原理
● bitmap:存储已merge的mapper id,防止重复merge
● position offset: 如果本次block没有正常merge,可以恢复到上一个block的位置
● currentMapId:标识当前正在append的block,保证不同mapper的block能依次 append
4.4 Magnet可靠性
1)如果Map task输出的Block没有成功Push到magnet上,并且反复重试仍然失败,则reduce task直接从ESS上拉取原始block数据
2)如果magnet上的block因为重复或者冲突等原因,没有正常完成merge的过程,则reduce task直接拉取未完成merge的block
3)如果reduce拉取已经merge好的block失败,则会直接拉取merge前的原始block
4)本质上,magnet中维护了两份shuffle数据的副本
4.5 Cloud Shuffle Service 思想
4.6 Cloud Shuffle Service 架构
▪️ Zookeeper WorkerList [服务发现]
▪️ Css Worker [Partitions / Disk I Hdfs]
▪️ Spark Driver [ 集成启动 CSs Master]
▪️ Css Master [Shuffle 规划/统计]
▪️ CSs ShuffleClient [Write 1 Read]
▪️ Spark Executor [Mapper + Reducer]
4.6.1 Cloud Shuffle Service 写入流程
4.6.2 Cloud Shuffle Service读取流程
4.6.3 Cloud Shuffle Service AQE
一个Partition会最终对应到多个Epoch file每个EPoch目前设置是512MB
4.7实践案例- CSS优化
● XX业务小时级任务(1.2w cores)
● 混部队列2.5h ->混部队列+CSS 1.3h (50%提升)