Spark Shuffle(基础)过程

介绍

Shuffle过程是进行数据的重组和排序,这是一个非常消耗资源的过程,包括磁盘IO、数据序列化和反序列化、网络IO。在Spark中Shuffle定义为一系列map任务和reduce任务,map任务负责组织数据,通常称为Shuffle Write,reduce任务负责聚合统计数据,通常称为Shuffle Read。
Spark Shuffle行为和Hadoop MapReduce中Shuffle过程基本一致,Map端为Shuffle提供数据,Reduce端负责接收Shuffle生成的数据。用户提交的Spark程序会被解析成DAG图,根据RDD依赖关系划分成多个Stage,Shuffle就发生在Stage之间。

Shuffle方式的发展史

Spark 1.1版本和以前版本默认是Hash Based Shuffle
Spark1.2 版本默认是Sort Based Shuffle
Spark2.0 版本废弃Hash Based Shuffle
Spark 2.1 版本三种Shuffle Writer:BypassMergeSortShuffleWriter、SortShuffleWriter、UnsafeShuffleWriter

Hash Based Shuffle V1

Hash Shuffle最初版本中,按照Hash方式进行分区,每一个map任务会产生reduce个数的临时文件,最终会生成map x reduce个临时文件,因此大量临时文件为CPU和磁盘带来了巨大的消耗,称为了计算的瓶颈。
Task运行过程:
Shuffle Write:在map任务中按照数据key取hash值,并对reduce个数取余运算,决定数据进入哪个reduce中。当内存buffer(32K)满了,就将数据批量写入磁盘文件,每个reduce对应一个磁盘文件,因此每个map任务都有reduce个磁盘文件。

假设Shuffle有100个map,100个reduce,最终会生成100 x 100 = 10000个磁盘文件。假设运行在10个Executor中,每个Executor分配10个任务,产生1000个磁盘文件。

Shuffle Read:在Stage初始阶段读取Shuffle Write产生的文件,每个reduce任务读取自身对应的磁盘文件。不同于MapReduce的Shuffle,这个reduce是边拉取边计算,拉取过来的数据先写入内存缓存中,然后在内存中进行聚合计算。
Hash Shuffle V1版本不涉及排序,最大的问题就是产生的临时磁盘文件过多,对网络、磁盘IO、CPU造成过大负载。

Hash Based Shuffle V2

为了解决临时文件过多问题,在Hash Shuffle V2版本中做了相关优化。在新版本中,将V1版本中每个map任务生成reduce个文件,优化成每个Core生成reduce个文件,这样在一定程度上减少了临时文件数量。

假设Shuffle有100个map,100个reduce,运行在10个Core中,最终生成10 x 100 = 1000个文件。假设运行在10个Executor中,每个Executor分配10个任务,产生100个文件。相对于之前版本文件数量降低了很多,但是如果reduce任务个数过多,同样会面临临时文件过多产生的开销问题。

BypassMergeSortShuffleWriter

该方式和Hash Shuffle类似,唯一区别就是会将map生成的多个临时文件合并成一个大文件。每个分区分配一个临时文件,按照key的Hash算法,将数据写入对应的分区文件,最终所有分区数据合并称为一个大文件,然后生成一个分区索引文件,通过索引可快速定位对应分区数据。

触发条件:
map端没有聚合操作(即不使用聚合类算子),且分区数必须小于200
相关参数:spark.shuffle.sort.bypassMergeThreshold

UnsafeShuffleWriter

UnsafeShuffleWriter通过ShuffleExternalSorter对数据进行外部排序,首先由ShuffleInMemorySorter根据partition id进行内存排序,然后排序后数据序列化压缩写入磁盘文件相应位置(FileSegment)。不断利用ShuffleInMemorySorter进行拉取数据、排序、刷盘,最终生成一个全局分区有序的大文件。

触发条件:
1、没有聚合算子或key排序
2、原始数据首先被序列化处理,并且再也不需要反序列,在其对应的元数据被排序后,需要Serializer支持relocation,在指定位置读取对应数据
3、分区数目必须小于 16777216 ,因为 partition number 使用24bit 表示
4、因为每个分区使用 27 位来表示 record offset, 所以一个 record 不能大于这个值

SortShuffleWriter

SortShuffleWriter维护一块内存,数据先写入内存中。内存结构包含Map和Array两种,当进行聚合排序操作时,使用Map数据结构存储数据,否则采用Array结构存储数据。如果内存不足时,会将数据溢写到磁盘临时文件。
ExternalSorter是SortShuffleWriter中排序工具,如果在map端聚合,会在map端进行聚合和排序,所有分区和分区内数据都有序。如果不在map端聚合,只会分区数据有序,分区内数据不排序。如需要分区内数据也有序,调用sortByKey在reduce端进行排序。

触发条件:
BypassMergeSortShuffleWriter和UnsafeShuffleWriter条件不满足时,由SortShuffleWriter输出map task结果。

Shuffle相关参数

参数 默认值 说明
spark.shuffle.compress true map输出数据压缩
spark.shuffle.spill.compress true 磁盘文件压缩
spark.shuffle.file.buffer 32K 内存缓冲区大小
spark.shuffle.sort.bypassMergeThreshold 200 bypass机制触发条件。当reduce task(Shuffle Read)数量小于200时,默认采用bypass机制
spark.reducer.maxSizeInFlight 48m reduce task从map task拉取数据最大值
spark.shuffle.service.enabled false 开启外部Shuffle Service,此服务负责保存Executor产生的shuffle文件。使用此参数之前,必须将spark.dynamicAllocation.enabled 设置为 “true”,开启动态资源调度

有关动态资源调度(Dynamic Resource Allocation)配置请参考Dynamic Resource Allocation

水平一般,能力有限,大数据小学生一枚。文章主要用于个人学习和总结,如果能给他人带来帮助,纯属意外。

你可能感兴趣的:(Spark)