一:为什么需要Sort-Based Shuffle?
1, Shuffle一般包含两个阶段任务:
第一部分:产生Shuffle数据的阶段(Map阶段,额外补充,需要实现ShuffleManager中的getWriter来写数据(数据可以通过BlockManager写到Memory,Disk,Tachyon等,例如想非常快的Shuffle,此时可以考虑把数据写在内存中,但是内存不稳定,所以可以考虑增加副本。建议采用MEMONY_AND_DISK方式);
第二部分:使用Shuffle数据的阶段(Reduce阶段,额外补充,Shuffle读数据:需要实现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;
二:Hash-based Shuffle
1, Spark Shuffle在最开始的时候只支持Hash-based Shuffle:默认Mapper阶段会为Reducer阶段的每一个Task单独创建一个文件来保存该Task中要使用的数据。
优点:就是操作数据简单。
缺点:但是在一些情况下(例如数据量非常大的情况)会造成大量文件(M*R,其中M代表Mapper中的所有的并行任务数量,R代表Reducer中所有的并行任务数据)大数据的随机磁盘I/O操作且会形成大量的Memory(极易造成OOM)。
2,Hash-based Shuffle产生的问题:
第一:不能够处理大规模的数据
第二:Spark不能够运行在大规模的分布式集群上!
3,Consolidate机制:
后来的改善是加入了Consolidate机制来将Shuffle时候产生的文件数量减少到C*R个(C代表在Mapper端,同时能够使用的cores数量,R代表Reducer中所有的并行任务数量)。但是此时如果Reducer端的并行数据分片过多的话则C*R可能已经过大,此时依旧没有逃脱文件打开过多的厄运!!!Consolidate并没有降低并行度,只是降低了临时文件的数量,此时Mapper端的内存消耗就会变少,所以OOM也就会降低,另外一方面磁盘的性能也会变得更好。
Spark在引入Sort-Based Shuffle之前,适合中小型数据规模的大数据处理!
三:Sort-Based Shuffle
1, 为了让Spark在更大规模的集群上更高性能处理更大规模的数据,于是就引入了Sort-based Shuffle!从此以后(Spark1.1版本开始),Spark可以胜任任何规模(包括PB级别及PB以上的级别)的大数据的处理,尤其是钨丝计划的引入和优化,Spark更快速的在更大规模的集群处理更海量的数据的能力推向了一个新的巅峰!
2, Spark1.6版本支持最少三种类型Shuffle:
// Let the user specify short names for shuffle managers
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")
val shuffleMgrName = conf.get("spark.shuffle.manager", "sort")
val shuffleMgrClass = shortShuffleMgrNames.getOrElse(shuffleMgrName.toLowerCase, shuffleMgrName)
val shuffleManager = instantiateClass[ShuffleManager](shuffleMgrClass)
实现ShuffleManager接口可以根据自己的业务实际需要最优化的使用自定义的Shuffle实现;
3,Spark1.6默认采用的就是Sort-based Shuffle的方式:
val shuffleMgrName = conf.get("spark.shuffle.manager", "sort")
上述源码说