这是我参与「第四届青训营 」笔记创作活动的第7天。
本课程主要分为四个部分:
- shuffle概述:shuffle是什么,shuffle的基本过程是什么
- spark中的shuffle算子:学习使用spark中的会产生shuffle的算子,了解其基本特性
- spark中的shuffle过程:spark中shuffle的核心原理和实现细节
- Push based shuffle:push shuffle社区的实现方案以及字节自己的实现方案
MapReduce概述
map:将大量数据分成几份,分散在不同机器上处理,该过程称为分布式处理
shuffle:每份数据会有不同分颜色,我们要对不同数据分别处理,将相同颜色的数据放在一起
reduce:然后继续处理这几份数据
shuffle算子
shuffle算子分类:
repartition:重新改变分区
ByKey:给定一个key,把key聚合在一起的算子
join算子:把本身没有在一起的数据按照某种条件放在一起
Spark中对shuffle的抽象-宽依赖,窄依赖
shuffleDependency如何产生,在产生Cogroup后,cogroup 由调用fullOuterJoin,join产生
Shuffle Dependency
-
创建会产生shuffle的RDD时,RDD会创建Shuffle Dependency来描述Shuffle相关的信息
-
构造函数
- A single key-value pair RDD, i.e. RDD[Product2[K, V]],一个key-value的RDD
- Partitioner (available as partitioner property),给定义和k,创建一个叫k的分区
- Serializer,把一个对象映射为一个数据流,或者相反操作
- Optional key ordering (of Scala’s scala.math.Ordering type),k是否需要排序的flag
- Optional Aggregator,
- mapSideCombine flag which is disabled (i.e. false) by default.
Partitioner
Aggregator
shuffle过程
shuffle历史
Hash Shuffle - 写数据
写数据优化:
Sort Shuffle -写数据
Shuffle - 读数据
Shuffle过程的触发流程
以上图为例,当执行collect时,它是个action算子触发计算,在collect会调用我们的Spark中的Task scheduler的submitJob,在Task scheduler中会分析counts的依赖关系,在查找依赖关系时会创建shuffle RDD对象,这个对象Task scheduler调用GetDependencies时,就会真正去创建shuffleDependencies,然后向shuffleManager注册自己。 后续会生成两个stage:map,reduce,然后这两个stage会提交给Executor。
Shuffle Handle的创建
在Register Shuffle时做的最重要的事情时根据不同条件创建不同的shuffle Handle。
不同的shuffle Handle对应不同的shuffle Writer
BypasssMergeShuffleWriter
UnsafeShuffleWriter
SortShuffleWriter
Reader实现 网络时序图
reader 的执行流程中需要网络请求,网络传输如下:
Reader实现 - ShuffleBlockFetchIterator
Reader实现 - External Shuffle Service
目的:为了解耦数据计算和数据服务,Spark支持单独的服务来处理这些请求
在使用reduce读取时,访问网络获取shuffle数据的这一部分使用的是ESS。
为了解决Executor为了服务数据的fetch请求导致无法退出问题,我们在每个节点上部署一个External Shuffle Service,这样产生数据的Executor在不需要继续处理任务时,可以随意退出。
在shuflle中时用到的技术:
Shuffle 优化使用的技术 - Zero Copy
减少用户态与内核态的切换,减少cpu拷贝
Netty 零拷贝
- 可堆外内存,避免 JVM 堆内存到堆外内存的数据拷贝。
- CompositeByteBuf 、 Unpooled.wrappedBuffer、 ByteBuf.slice ,可以合并、包装、切分数组,避免发生内存拷贝
- Netty 使用 FileRegion 实现文件传输,FileRegion 底层封装了 FileChannel#transferTo() 方法,可以将文件缓冲区的数据直接传输到目标 Channel,避免内核缓冲区和用户态缓冲区之间的数据拷贝
shuffle优化
-
避免shuffle ——使用broadcast替代join
-
//传统的join操作会导致shuffle操作。 //因为两个RDD中,相同的key都需要通过网络拉取到一个节点上,由一个task进行join操作。 val rdd3 = rdd1.join(rdd2) //Broadcast+map的join操作,不会导致shuffle操作。 //使用Broadcast将一个数据量较小的RDD作为广播变量。 val rdd2Data = rdd2.collect() val rdd2DataBroadcast = sc.broadcast(rdd2Data) //在rdd1.map算子中,可以从rdd2DataBroadcast中,获取rdd2的所有数据。 //然后进行遍历,如果发现rdd2中某条数据的key与rdd1的当前数据的key是相同的,那么就判定可以进行join。 //此时就可以根据自己需要的方式,将rdd1当前数据与rdd2中可以连接的数据,拼接在一起(String或Tuple)。 val rdd3 = rdd1.map(rdd2DataBroadcast...) //注意,以上操作,建议仅仅在rdd2的数据量比较少(比如几百M,或者一两G)的情况下使用。 //因为每个Executor的内存中,都会驻留一份rdd2的全量数据。 复制代码 -
使用可以map-side预聚合的算子
-
Shuffle 参数优化
- spark.default.parallelism && spark.sql.shuffle.partitions
- spark.hadoopRDD.ignoreEmptySplits
- spark.hadoop.mapreduce.input.fileinputformat.split.minsize
- spark.sql.file.maxPartitionBytes
- spark.sql.adaptive.enabled && spark.sql.adaptive.shuffle.targetPostShuffleInputSize
- spark.reducer.maxSizeInFlight
- spark.reducer.maxReqsInFlight spark.reducer.maxBlocksInFlightPerAddress
-
Shuffle 倾斜优化
-
什么叫倾斜?有什么危害
-
解决倾斜方法举例
- 增大并发度
- AQE
-
为什么要PUSH Shuffle?
-
上一部分所讲的shuffle过程存在哪些问题?
- 数据存储在本地磁盘,没有备份
- IO 并发:大量 RPC 请求(M*R)
- IO 吞吐:随机读、写放大(3X)
- GC 频繁,影响 NodeManager
Magnet主要流程
Magnet实现原理
server合并数据的细节:
magnet server在给数据合并时会给每个partition维护一些原数据,这份原数据由applicaiton id,shuffle id和 partition id 混合而成。
当server接受到一个block块时。会尝试merge这个文件,在merge前会检索信息,通过这些原信息来正确处理一些潜在的问题和场景。
Magnet可靠性
Cloud Shuffle Service思想
Cloud Shuffle Service架构
Cloud Shuffle Service 读写流程
Cloud Shuffle Service 支持AQE
在聚合文件时主动将文件切分为若干块,当触发AQE时,按照已经切分好的文件块进行拆分。
总结:
- 1.学习了MapReduce和shuffle基础
- 2.学习了shuffle算子以及构造方法,学习了shuffle的流程以及写数据和读数据
- 3.学习了shuffle的优化以及具体的优化方法Magnet和Cloud Shuffle Service
标题:大数据 Shuffle 原理与实践 | 青训营笔记
网址:juejin.cn/