一 、为什么需要sort-based-shuffle?
1.shuffle一般包含两阶段任务:
第一部分,产生shuffle数据的阶段(map阶段,额外补充,需要实现ShuffleManager中getWriter来写数据,数据可以利用BlockManager写到memory,disk,tachyon等,例如想非常快的shuffle,此时可以考虑把数据写在内存中,但是内存不稳定,建议采用MEMORY_AND_DISK方式);
第二部分,使用shuffle数据的阶段(reduce阶段,额外补充,需要实现shuffleManager的getReader,Reader回向Dirver获取上一个stage中的shufflemaptask产生的数据);
2.spark的job会被划分成很多stage。
Spark是链式的
如果只有一个stage,则这个job就相当于只有一个mapper极端。当然不会产生shuffle,适合于简单的ETL(将数据从来源端经过抽取(extract)、转换(transform)、加载(load)至目的端的过程);
如果不止一个stage,则最后一个stage就是最终的reducer,最左侧的第一个stage就仅仅是整个job的mapper,中间所有的任意一个stage是其父stage的reducer且是其子的mapper;
3.spark shuffle在最开始的时候只支持hash-based shuffle;默认mapper阶段会为reducer阶段的每一个task单独创建一个文件来保存该task中要使用的数据,但是在一些情况下(例如数据量非常大的情况)会造成大量文件(M*R,其中M代表mapper中的所有的并行任务数量,R代表reducer中所有的并行任务数量)的随机磁盘IO操作且会形成大量memory消耗(极易造成oom),这是致命的问题,因为第一不能处理大规模的数据,第二spark不能运行在大规模的分布式集群上!后来的改善方案是加入shuffle consalidata机制来将shuffle时候产生的文件数量减少到C*R个(C代表在mapper端同时能够使用的cores的数量,R代表reducer中所有的并行任务数量),但是此时如果reducer端的并行分片过多的话,则C*R可能依旧过大,此时依旧没有逃脱文件打开的厄运!!!
Spark在引入sort-base shuffle(spark 1.1版本以前)以前比较适合中小规模的大数据处理!
4.为了让spark在更大规模的集群上更高性能处理更大规模的数据,于是引入了sort-base shuffle!从此以后(spark 1.1版本开始)spark可以胜任任意规模(包含PB级别及以上)的大数据的处理,尤其是随着钨丝计划的引入和优化,把spark更快速的在更大规模的集群处理更海量的能力推向了一个新的巅峰!
5.Spark 1.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”)
val shuffleMgrName
val shuffleMgrClass
val shuffleManager
实现ShuffleManager接口可以根据自己的业务实际需要最优化的使用自定义的Shuffle实现;
6.Spark 1.6默认采用的就是Sort-based Shuffle的方式。
val shuffleMgrName=conf.get(“spark-shuffle-mangager”,”sort”)
“`
上述的源码说明,你可以在spark的配置文件中配置spark框架运行时要使用的具体的ShuffleManager的实现。
修改conf/spark-default.conf, 加入如下内容
spark.shuffle.manager SORT
sort-base shuffle不会为每个reducer中的task生成一个单独的文件,相反,sort-base shuffle会把mapper中每个ShuffleMapTask所有的数据data只写到一个文件中,因为每个shufflemaptask中的数据会被分类,所以sort-base shuffle使用了index文件存储具体shufflemaptask输出数据在同一个data文件中是如何分类的信息!!!所以说基于sort-base shuffle会在mapper中的每个shufflemaptask中产生两个文件:data文件和index文件,其中data文件是存储当前task的shuffle输出,index文件则存储data文件中数据通过partition的分类信息(或这个索引文件将记录不同partition的range),此时下一个阶段的stage中的task就是根据这个index获取自己所要抓取的上一个stage中的shufflemaptask产生的数据;
Ps:Shuffle Map Task会按照key相对应的partition ID进行sort,其中属于同一个partition的key不会sort。
Sort-based Shuffle产生的文件的数量正确的答案是 2M。Sort-based Shuffle会产生2M(M代表mapper阶段中并行的partition的总数量,其实就是mapper端shufflemaptask的总数量)个shuffle临时文件!!!
回顾整个shuffle的历史,shuffle产生的临时文件的数量的变化依次为:
1)Basic Hash Shuffle:M*R;
2)Consalidata方式的HashShuffle:C*R;
3)Sort-base Shuffle:2M;
二 、在集群中动手实战Sort-Based Shuffle
在Sort-Based Shuffle的中reducer是如何获取自己需要的数据呢?具体而言,reducer首先找driver去获取父stage中每个shufflemaptask输出的位置信息,根据位置信息获取index文件,解析index文件,从解析的index文件中获取data文件中属于自己的那部分文件。
三、 默认Sort-Based Shuffle的几个缺陷
1, 如果Mapper中task的数量过大,依旧会产生很多小文件;此时在shuffle传递数据的过程到reducer端,reduce会需要同时大量的记录来进行反序列化,导致大量的内存消耗和gc的巨大负担,造成系统缓慢甚至崩溃。
2, 如果需要在分片内也要进行排序的话,此时需要进行mapper和reducer端的两侧排序!!!