Spark Sort-Based Shuffle内幕彻底解密
1、为什么使用Sort-Based Shuffle内幕彻底解密
2、Sort-Based Shuffle实战
3、Sort-Based Shuffle内幕
4、Sort-Based Shuffle的不足
1、为什么使用Sort-Based Shuffle
1)Shuffle一般包含两阶段任务,第一部分,产生Shuffle数据的阶段(Map阶段,额外补充,需要实现ShuffleManager中getWriter来写数据(数据可以BlockManager写到Memory、Disk、Tachyon等,例如想非常快的Shuffle,此时可以考虑把数据写在内存中,但是内存不稳定,建议采用MEMORY_AND_DISK方式));第二部分,使用Shuffle数据的阶段(Reduce阶段,额外补充,需要实现ShuffleManager的getReader,Reader会向Driver获取上一个Stage产生的Shuffle数据)
2)Spark的Job会被划分成很多Stage:
如果只有一个Stage,则这个Job就相当于只有一个Mapper阶段,当然不会产生Shuffle,适合于简单的ETL;
如果不止一个Stage,则最后一个Stage就是最终的Reducer,最左侧的第一个Stage就仅仅是整个Job的Mapper,中间所有的任意一个Stage是其父Stage的Reducer,且是其子Stage的Mapper
3)Spark Shuffle在最开始只支持Hash-based Shuffle:默认Mapper阶段会为Reducer阶段的每一个Task单独创建一个文件来保存该Task中要使用的数据,但是会在一些情况下(例如数据量非常大的情况)会造成大量文件(M*R,其中M代表Mapper中的所有的并行任务数量,R代表Reducer中所有的并行任务数量)的随机磁盘I/O操作,且会形成大量的Memory消耗(极易造成OOM),这是致命的问题,因为第一不能够处理大规模的数据,第二Spark不能够运行在大规模的分布式集群上!后来的改善方案是加入了Shuffle Consolidate机制来将Shuffle时候产生的文件数量减少到C*R个(C代表在Mapper端同时能够使用的Cores的数量,R代表Reducer中所有的并行任务数量),但是此时如果Reducer端的并行数据分片过多的话,则C*R可能依旧过大,此时依旧没有逃脱文件打开过多的厄运!
Spark在引入Sort-based Shuffle(Spark1.1版本以前)以前比较适用于中小规模的大数据处理!
4)为了让Spark在更大规模的集群上更高性能处理更大规模的数据,于是就引入了Sort-based Shuffle!从此以后(Spark1.1版本开始),Spark可以胜任任意规模(包含PB级别及PB以上的级别)的大数据的处理,尤其是随着钨丝计划的引入和优化,把Spark更快速的在更大规模的集群处理更海量的数据的能力推向了一个新的巅峰!
5)Spark1.6版本支持至少三种类型Shuffle
val shortShuffleMgrNames = Map(
“hash” -> “org.apache.spark.shuffle.hash.HashShuffleManager”,
“sort” -> “org.apache.spark.shuffle.sort.SortShuffleManager”,
“tungsten-sort” -> “org.apache.spark.shuffle.sort.SortShuffleManager”)
实现ShuffleManager接口可以根据自己的业务实际需要最优化的使用自定义的Shuffle实现。
6)Spark1.6默认采用的就是Sort-based Shuffle的方式
val shuffleMgrName= conf.get(“spark.shuffle.manager”,”sort”)
上述的源码说明:你可以在Spark的配置文件中配置Spark框架运行时要使用的具体的ShuffleManager的实现
修改conf/spark-default.conf,加入以下内容:spark.shuffle.manager SORT 具体ShuffleManager方式(Sort、Hash、钨丝等)
Sort-based Shuffle 不会为每个Reducer中的Task生成一个单独的文件,相反,Sort-based Shuffle会把Mapper中每个SuffleMapTask所有的输出数据Data只写到一个文件中,因为每个ShuffleMapTask中的数据会被分类,所以Sort-based Shuffle使用index文件存储具体ShuffleMapTask输出数据在同一个Data文件中是如何分类的信息!!所以说基于Sort-based的Shuffle会在Mapper中的每一个ShuffleMapTask中产生两个文件:Data文件和Index文件,其中Data文件是存储当前Task的Shuffle输出的,Index文件中则存储了数据Data文件中数据通过Partitioner的分类信息,此时下一个阶段的Stage中的Task就是根据这个Index文件获取自己所要抓取的上一个Stage中的ShuffleMapTask产生的数据的。
Sort-based Shuffle会产生2M(M代表Mapper阶段中并行的Partition的总数量,其实就是Mapper端ShuffleMapTask的总数量)个Shuffle临时文件
回顾整个Shuffle的历史,Shuffle产生的临时文件的数量的变化依次为:
Basic Hash Shuffle:M*R;
Consalidate方式的Hash Shuffle:C*R;
Sort-based Shuffle: 2M;
2、在集群中动手实战Sort-based Shuffle
通过动手实践确实证明了Sort-based Shuffle产生了2M个文件
在Sort-based Shuffle中的Reducer是如何获取自己需要的数据的呢?具体而言,Reducer首先找Driver去获取父Stage中每个ShuffleMapTask输出的位置信息,根据位置信息获取index文件,解析index文件,从解析的index文件中获取Data文件中属于自己的那部分内容
3、默认Sort-based Shuffle的几个缺陷
1)如果Mapper中Task的数量过大,依旧会产生很多小文件;此时在Shuffle传递数据的过程中到Reducer端,reduce会需要同时打开大量的记录来进行反序列化,导致大量的内存消耗和GC的巨大负担,造成系统缓慢甚至崩溃
2)如果需要在分片内进行排序的话,此时需要进行Mapper端和Reducer端的两次排序!!!
hadoop mapper有数组代表的环形内存缓存区(有数据也有索引)