这是我参与「第四届青训营 」笔记创作活动的第6天
在学习Spark的Shuffle流程之前,先要学习几个概念
- Shuffle Dependency:创建会产生Shuffle的RDD时,同时会创建一个ShuffleDependency来描述Shuffle相关的信息
- Aggregator:Shuffle时的性能优化器,提前在map端聚合,减少shuffle数据量
Spark Shuffle发展历史
现在我们跟着历史,一步一步学习Spark是怎么演进的。
我们先假设,有M个task,每个task有R个partitition,CPU核数为C个
Hash Based Shuffle Write V1.0
每一个Partitition都会把数据写到一个独立文件里,会生成M*R个文件
- 这样缺点很明显产生的中间数据文件太多了
Hash Based Shuffle Write V2.0
每个Partition把数据写入到一个文件片段,由于CPU核数有限,有M个task,但是同一时间并行跑的task只有C个,只生成C*R个文件
- 缺点:虽然CR < MR,但是依然会产生很多中间数据文件
Sort Based Shuffle Write
每个Map Task生成一个包含所有Partitition数据的文件。原来的HashShuffle每个task要给每个分区生一个文件,现在经过排序以后,一个task只需要生成一个文件
- 这样一共只需要生成M个文件,中间数据文件数量大幅减少
Shuffle Read
不管是HashShuffle还是SortShuffle,Reduce端读取的方法都差不多
每个reduce task分别拉取自己的片段
这个拉取数据的过程中,有一个External Shuffle Service (ESS) 服务,这个服务在每个节点上。
Writer端呢会把数据存储的文件位置告诉这个ESS服务,然后Reducer端要fetch数据的时候会提交请求给ESS服务,这样就把Writer和Reader两个操作给解耦开了。
Shuffle触发流程
上节课我们学过了RDD从创建到执行中间的流程,这里讲一下主要针对shuffle的流程
当RDD触发action算子后,会调用DAGScheduler的submitJob,这个过程会分析RDD的依赖关系,根据这个依赖关系创建对应的Shuffle对象,然后向ShuffleManager注册自己,之后生成stage,然后发送task去执行
那么shuffle对象是怎么创建注册的?
其实是根据RDD的信息去创建最合适的ShuffleHandle,然后使用ShuffleHandle去创建对应ShuffleWriter,我们刚刚讲过有很多种ShuffleWrite的方法,这里就可以根据实际的适用场景去选择最合适的ShuffleWrite方法。
Shuffle优化
接下来介绍几个可以优化Shuffle的方法
- 避免使用Shuffle,用broadcast+map代替join
-
使用map-side combine的算子
-
shuffle参数优化
- shuffle数据倾斜优化:提高并行度、AQE
至此,这节课的内容基本学习完毕,还有个Push Shuffle是业界最新的shuffle算法,感兴趣可以继续研究