这是我参与「第四届青训营 」笔记创作活动的的第7天
一、shuffle概述
1.MapReduce概述
·在开源实现的MapReduce中,存在Map、Shuffle、Reduce三个阶段。
Map阶段,是在单机上进行的针对一小块数据的计算过程,就是按照给定的方法进行筛选分类;
Shuffle 阶段,在map阶段的基础上,再把每个map处理好的数据再做移动,把相同颜色的数据放在一起,为后续的reduce阶段做准备,也就是说,map阶段将几个小块数据分类完成后,shuffle将同类型的数据进行合并;
Reduce阶段,对移动后的数据进行处理,依然是在单机上处理一小份数据,最终可以Shuffle得到的合并后的数据进行count,得到不同颜色的sum值。
2.Shuffle对性能的影响
- M*R次网络连接:每一个reduce都要访问所有的map来获取对应的数据,同样还有等次的网络请求
- 大量的数据移动:M*R次数据移动
- 数据丢失风险:在移动和计算的过程中,存在丢失的风险,一旦丢失,面临着数据的重算
- 可能存在大量的排序操作:需要在map阶段把不同的颜色数据区分开,放到不同位置需要对颜色进行排序
- 大量的数据序列化、反序列化操作:消耗大量cpu
- 数据压缩与解压缩:在存储大量数据过程中,压缩与解压缩也会占用大量CPU
3.总结
在大数据场景下,数据shuffle表示了不同分区数据交换的过程,不同的shuffle策略性能差异较大。目前在各个引擎中shuffle都是优化的重点,在spark框架中,shuffle是支持spark进行大规模复杂数据处理的基石。
二、shuffle算子
1.shuffle算子分类
Spark中会产生shuffle的算子大概可以分为四类
- repartition:把数据改变分区
- ByKey:给定k-v对,把key聚合到一起进行计算的算子
- join:把本身没有在一起的数据按照某种条件放在一起进行计算的算子
- distinct:特殊的ByKey操作
2.Spark中对shuffle的抽象-宽依赖、窄依赖
窄依赖: 父RDD的每个分片至多被子RDD中的一个分片所依赖。出现宽依赖会出现map stage和reduce stage,两个stage中间就会出现shuffle操作 宽依赖: 父RDD中的分片可能被子RDD中的多个分片所依赖
算子内部的依赖关系
Shuffle Dependency
- 宽依赖对应的对象是shuffle dependency,被两个RDD所创建,CoGroupedRDD和ShuffledRDD,创建会产生shuffle的RDD时,RDD会创建Shuffle Dependency来描述Shuffle相关的信息
shuffle Dependency构造 - Partitioner
- Partitioner
- 负责把key映射成一个数字,这个数字代表了某一个具体的分区。用来将record映射到具体的partition的方法
- 接口
- numberPartitions:一共有多少个分区
- getPartition:给一个key,说明具体的分区是哪个
- 经典实现
- HashPatitioner
- Aggregator
- 一个重要的性能优化器,在map侧合并部分record的函数
- 接口
- createCombiner:只有一个value的时候初始化的方法
- mergeValue:合并一个value到Aggregator中
- mergeCombiners:合并两个Aggregator 深入解读 Spark 宽依赖和窄依赖(ShuffleDependency & NarrowDependency)_因特马的博客-CSDN博客_spark的宽依赖和窄依赖 spark 2.3源码分析之ShuffleDependency_zhifeng687的博客-CSDN博客
三、Shuffle过程
1.Shuffle实现的发展历程
- Spark 0.8及以前Hash Based Shuffle
- Spark 0.8.1 为Hash Based Shuffle引入File Consolidation机制
- Spark 0.9 引入ExternalAppendOnlyMap
- Spark 1.1 引入Sort Based Shuffle,但默认仍为Hash Based Shufle
- 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退出历史舞台 简单分为两个阶段,Hash Shuffle和Sort Shuffle。
Hash Shuffle-写数据
每个partition会映射到一个独立的文件,最终生成m*r个文件,对文件系统造成的压力大。
Hash Shuffle-写数据优化
每个partition会映射到一个文件片段,最终得到c*r个文件,c是cpu的核数,打开的文件依然是r个,依然可能面临OOM的问题。
Sort shuffle:写数据
不在给每个partition一个buffer了,所有的数据写到一个buffer里,当内存满了,通过排序的方式,把相同的数据放在一起,所以叫sort shuffle,排序会消耗更多cpu,好处是每个task会创建一个包含所有partition数据的文件,还有一个index索引文件。
Shuffle-读数据
每个reduce task分别获取所有map task生成的属于自己的片段
4.shuffle过程的触发流程
Spark源码解读(6)——Shuffle过程_scalahome的博客-CSDN博客 shuffle过程详解_follweme888的博客-CSDN博客_shuffle过程
5.Shuffle Handle的创建
Register Shuffle时做的最重要的事情时根据不同条件创建不同的shuffle Handle
6.Shuffle Handle与Shuffle Writer的对应关系
7.Writer实现
Writer实现-BypassMerge Shuffle Writer
- 不需要排序,节省时间
- 写操作的时候会打开大量文件
- 类似于Hash Shuffle
Writer实现-UnsafeShuffleWriter
- 使用类似内存页储存序列化数据
- 数据写入后不再反序列化
对于堆外内存的管理,record序列化往对外内存写的时候,还会再堆内有一个long Array记录record属于哪个partition,写在了内存页d额第几页。Spill会触发排序,只发生在array上,不会排record。
- 只根据partition排序Long Array
- 数据不移动
Writer实现-SortShuffleWriter
- 支持combine
- 需要combine时,使用PartitionedAppendOnlyMap,本质是个HashTable
- 不需要combine时PartitionedPairBuffer本质是个array
【spark】关于spark的shuffle模式的一些见解_lsr40的博客-CSDN博客_shuffle形式有几种
8.Reader实现
网络时序图
- 基于netty的网络通信框架
- 位置信息记录在MapOutputTracker中
- 主要发送OpenBlocks请求和Chunk或Stream请求
ShuffleBlockFetchIterator
- 区分local和remote节省网络资源
- 防止OOM
External Shuffle Service
- ESS作为一个存在于每个节点上的agent,为所有Shuffle Reader提供服务,从而优化了Spark作业的资源利用率,MapTask在运行结束后可以正常退出
四、Push Shuffle
为什么需要Push Shuffle ?
- Avg IO size太小,造成了大量的随机IO,严重影响磁盘的吞吐
- M*R次读请求,造成大量的网络连接,影响稳定性
Push Shuffle的实现
- Facebook:cosco
- Linkdin:magnet
- Uber:Zeus
- Alibaba:RSS
- Tencent:FireStorm
- Bytedance:CSS
- Spark3.2:push based shuffle
[SPARK][CORE] 面试问题之 3.2新的特性Push-based Shuffle源码解析 - 腾讯云开发者社区-腾讯云 (tencent.com)
Magnet实现原理
- Spark driver组件,协调整体的shuffle操作
- map任务的shuffle writer过程完成后,增加了-个额外的操作push. merge,将数据复制一份推到远程shuffle服务上
- magnet shuffle service是一个强化版的ESS。将隶属于同一个shuffle partition的block,会在远程传输到magnet后被merge到一个文件中
- reduce任务从magnet shuffle service接收合并好的shuffle数据
- bitmap:存储已merge的mapper id,防止重复merge
- position offset:如果本次block没有正常merge,可以恢复到上一个block的位置
- currentMapld:标识当前正在append的block,保证不同mapper 的block能依次append
Magnet可靠性
- 如果Map task输出的Block没有成功Push到magnet上,并且反复重试仍然失败,则reduce task直接从ESS上拉取原始block数据
- 如果magnet上的block因为重复或者冲突等原因,没有正常完成merge的过程,则reducetask直接拉取未完成merge的block
- 如果reduce拉取已经merge好的block失败,则会直接拉取merge前的原始block本质上,magnet中维护了两份shuffle数据的副本