大数据Shuffle 原理与实践|青训营笔记

194 阅读6分钟

这是我参与「第四届青训营」笔记创作活动的的第6天

01.Shuffle概述

1.1 MapReduce概述

▪️ 2004年,谷歌发布了《MapReduce:Simplified Data Processing on Large Clusters》论文

▪️ 在开源实现的MapReduce中,存在Map、Shuffle、Reduce三个阶段。

截屏2022-07-31 13.09.23.png

1.1 Map阶段

Map阶段,是在单机上进行的针对一小块数据的计算过程

1.2 Shuffle阶段

Shuffle 阶段,在map阶段的基础上,进行数据移动,为后续的reduce阶段做准备。

1.3 Reduce过程

reduce阶段,对移动后的数据进行处理,依然是在单机上处理一小份数据

1.4 为什么shuffle对性能非常重要

▪️ M * R次网络连接

▪️ 大量的数据移动

▪️ 数据丢失风险

▪️ 可能存在大量的排序操作

▪️ 大量的数据序列化、反序列化操作

▪️ 数据压缩

截屏2022-07-31 13.13.59.png

总结

截屏2022-07-31 13.15.22.png

在大数据场景下,数据shuffle表示了不同分区数据交换的过程,不同的shuffle策略性能差异较大。目前在各个引擎中shuffle都是优化的重点,在spark框架中,shuffle 是支撑spark进行大规模复杂数据处理的基石。

02.Shuffle算子

2.1 Shuffle算子分类

Spark中会产生shuffle的算子大概分为4类

截屏2022-07-31 13.18.48.png

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中的多个分片所依赖

截屏2022-07-31 13.24.10.png

2.2算子内部的依赖关系

截屏2022-07-31 13.28.26.png

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会映射到一个独立的文件

截屏2022-07-31 13.47.39.png

3.1 Hash Shuffle - 写数据优化

每个partition会映射到一个文件片段

截屏2022-07-31 13.48.47.png

3.2 Sort shuffle:写数据

每个task生成一个包含所有partiton数据的文件

截屏2022-07-31 13.49.51.png

3.3 Shuffle - 读数据

每个reduce task分别获取所有map task生成的属于自己的片段

3.4 Shuffle过程的触发流程

截屏2022-07-31 13.51.37.png

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

截屏2022-07-31 13.54.17.png

3.6 Shuffle Handle与Shuffle Writer的对应关系

截屏2022-07-31 13.55.05.png

3.7 Writer FII - BypassMergeShuffle Writer

● 不需要排序,节省时间

● 写操作的时候会打开大量文件

● 类似于Hash Shuffle

截屏2022-07-31 13.56.30.png

● 使用类似内存页储存序列化数据

● 数据写入后不再反序列化

截屏2022-07-31 13.57.00.png

3.7 Writer实现- UnsafeShuffle Writer

截屏2022-07-31 13.58.57.png

● 只根据partition 排序Long Array

● 数据不移动

3.7 Writer FIЛ - SortShuffle Writer

▪️ 支持 combine

▪️ 需要combine时, 使用PartitionedAppendOnlyMap, 本质是个HashTable

▪️ 不需要combine时,PartitionedPairBuffer本质是个array

截屏2022-07-31 14.02.36.png

3.8 Reader实现 - 网络时序图

▪️ 使用基于netty的网络通信框架

▪️ 位置信息记录在MapOutputTracker中

▪️ 主要会发送两种类型的请求

1)OpenBlocks请求 2)Chunk请求或Stream请求

截屏2022-07-31 14.04.50.png

3.8 Reader 实现 - ShuffleBlockFetchIterator

1)区分local和remote节省网络消耗

2)防止 OOM

▪️ maxBytesInFlight

▪️ maxReqsInFlight

▪️ maxBlocksInFlightPer Address

▪️ maxReqSizeShuffleToMem

▪️ maxAttemptsOnNettyOOM

截屏2022-07-31 14.07.30.png

3.8 Read 实现 - External Shuffle Service

ESS作为一个存在于每个节点上的agent为所有Shuffle Reader提供服务,从而 优化了Spark作业的资源利用率,MapTask在运行结束后可以正常退出。

截屏2022-07-31 14.08.52.png

3.9 Shuffle优化使用的技术Zero Copy

截屏2022-07-31 14.09.39.png

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

截屏2022-07-31 14.13.17.png

3.11 Shuffle优化

使用可以map-side预聚合的算子

截屏2022-07-31 14.14.10.png

截屏2022-07-31 14.14.33.png

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导致作业失败

截屏2022-07-31 14.17.42.png

3.13常见的倾斜处理办法

● 提高并行度

● 优点:足够简单

● 缺点:只缓解、不根治

截屏2022-07-31 14.18.51.png

3.13 Spark AQE Skew Join

AQE根据shuffle文件统计数据自动检测倾斜数据,将那些倾斜的分区打散成小的子分区,然后各自进行join。

截屏2022-07-31 14.20.20.png

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

截屏2022-07-31 14.21.34.png

3.15 参数调整

▪️ spark.sql.adaptive.shuffle. targetPostShuffleInputSize :

▪️ spark.sql.files.maxPartitionBytes :

截屏2022-07-31 14.22.50.png

04. Push Shuffle

4.1为什么需要Push Shuffle ?

▪️ Avg IO size太小,造成了大量的随机IO,严重影响磁盘的吞吐

▪️ M*R次读请求,造成大量的网络连接,影响稳定性

截屏2022-07-31 14.24.49.png

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数据

截屏2022-07-31 14.28.31.png

4.3 Magnet实现原理

● bitmap:存储已merge的mapper id,防止重复merge

● position offset: 如果本次block没有正常merge,可以恢复到上一个block的位置

● currentMapId:标识当前正在append的block,保证不同mapper的block能依次 append

截屏2022-07-31 14.29.57.png

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 思想

截屏2022-07-31 14.32.09.png

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]

截屏2022-07-31 14.35.07.png

4.6.1 Cloud Shuffle Service 写入流程

截屏2022-07-31 14.36.11.png

4.6.2 Cloud Shuffle Service读取流程

截屏2022-07-31 14.37.03.png

4.6.3 Cloud Shuffle Service AQE

一个Partition会最终对应到多个Epoch file每个EPoch目前设置是512MB

截屏2022-07-31 14.38.26.png

4.7实践案例- CSS优化

● XX业务小时级任务(1.2w cores)

● 混部队列2.5h ->混部队列+CSS 1.3h (50%提升)

截屏2022-07-31 14.39.47.png