本期内容:
1 为什么使用Sort-Based Shuffle2 Sort-Based Shuffle 实战3 Sort-Based Shuffle 内幕4 Sort-Based Shuffle的不足
一、为什么需要Sort Based Shuffle?
1、
Shuffle一般包含两阶段任务;第一部分,产生Shuffle数据的阶段(map阶段)getWriter
,需要实现ShuffleManager中getWriter来写数据(数据
可以BlockManager写到Memory、DIsk、Tachyon等,例如像非常快的Shuffle,此时可以考虑把数据写到内存中,但是内存不稳定,建议使用
MEMORY_AND_DISK
方式)
,第二阶
段,使用Shuffle数据的阶段(Reduce阶段)
,额外补充,需要实现ShuffleManager的
getReader;
-
- def
registerShuffle[K, V, C]( -
shuffleId: Int, -
numMaps: Int, -
dependency: ShuffleDependency[K, V, C]): ShuffleHandle -
- def
getWriter[K, V](handle: ShuffleHandle, mapId: Int, context: TaskContext): ShuffleWriter[K, V] -
- def
getReader[K, C]( -
handle: ShuffleHandle, -
startPartition: Int, -
endPartition: Int, -
context: TaskContext): ShuffleReader[K, C]
2、如果只有一个Stage,则这个Job就是相当于只有一个Mapper阶段, 当然不会产生Shuffle,适合于简单的ETL;
在Spark的链条上有很多的
Stage,最后一个Stage产生阶段,最后一个Stage是倒数第二个Stage的Reducer(除了单Stage的job),最后一个Stage是Reducer,第一个
Stage一定是Mapper,中间的Stage既是Mapper也是Reducer,即第n个Stage是第n-1个Stage的Reducer,也是第n+1个Stage的Mapper。
3、Spark Shuffle在最开始的时候只支持 Hash-Based Shuffle;默认Mapper阶段会为Reducer阶段的每一个Task单独创建一个文件来保存该Task
中要使用的数据,但是在一些情况下
(例如数据量非常大的情况)会造成大量的(M*R,之中M代表Mapper中所有的并行任务数量,R代表
Reducer中所有的并行任务数据)随机磁盘I/O操作且会造成大量的MEM消耗(极易造成
OOM),这是致命的,因为:
说明:Spark在引入Sort-Based Shuffle以前,比较适用于中小规模的大数据处理(Spark1.1X)
4、
为了让Spark在更大规模的集群上更高性能处理更大规模的数据,于是就引入了Sort-Based Shuffle!从此以后(Spark1.1.X版本开始),
Spark可以胜任任意规模(包含PB以及以上级别的)的更大数据的处理,尤其是随着钨丝计划的引入和优化,把Spark更快的扎起更大的集群
处理更加海量的数据的能力推向了一个新的巅峰!
5、Spark1.6版本支持至少三种类型的Shuffle(可以自定义,Shuffle可插拔等),实现ShuffleManager接口可以根据自己的需要更优化的自定义的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)
- val
shuffleMgrName = conf.get("spark.shuffle.manager", "sort")
上述的源码说明,你可以在Spark的配置文件中配置Spark框架运行时要使用的具体的ShuffleManager的实现。
二、在集群中动手实战Sort-based Shuffle
1、启动Hadoop HDFS
- root@Master:/usr/local/hadoop/hadoop-2.6.0/sbin#
./start-dfs.sh
- root@Master:/usr/local/hadoop/hadoop-2.6.0/sbin#
hadoop dfs -mkdir /library/dataForSort
- root@Master:/usr/local/hadoop/hadoop-2.6.0#
hadoop dfs -put README.txt /library/dataForSort - DEPRECATED:
Use of this script to execute hdfs command is deprecated. - Instead
use the hdfs command for it. - root@Master:/usr/local/hadoop/hadoop-2.6.0#
hadoop dfs -put LICENSE.txt /library/dataForSort - DEPRECATED:
Use of this script to execute hdfs command is deprecated. - Instead
use the hdfs command for it. - root@Master:/usr/local/hadoop/hadoop-2.6.0#
hadoop dfs -put NOTICE.txt /library/dataForSort - DEPRECATED:
Use of this script to execute hdfs command is deprecated. - Instead
use the hdfs command for it. - root@Master:/usr/local/hadoop/hadoop-2.6.0#
- start-all.sh
stop-mesos-dispatcher.sh - start-history-server.sh
stop-mesos-shuffle-service.sh - start-master.sh
stop-shuffle-service.sh - start-mesos-dispatcher.sh
stop-slave.sh - start-mesos-shuffle-service.sh
stop-slaves.sh - start-shuffle-service.sh
stop-thriftserver.sh - root@Master:/usr/local/spark/spark-1.6.0-bin-hadoop2.6/sbin#
./start-all.sh
- root@Master:/usr/local/spark/spark-1.6.0-bin-hadoop2.6/bin#
./spark-shell
- scala>sc.textFile("/library/dataForSort").flatMap(_.split("
")).map(word =>(word,1)).reduceByKey(_+_).count
Shuffle运行生成的中间的文件(共8个文件,data4个,index4个):
- shuffle_0_0_0.data
- Shuffle_0_1_0.data
- Shuffle_0_2_0.data
- Shuffle_0_3_0.data
- Shuffle_0_0_0.index
- Shuffle_0_1_0.index
- Shuffle_0_2_0.index
- Shuffle_0_3_0.index
命名规则:第一个1代表Stage,第二个1代表ShuffleMapTask ID,第三个0代表Reducer的ID,data代表数据本身
由此证明了在默认的Sort-based Shuffle的产生2M个文件,其中M为Task的数量。
一般merge 排序的时候为了减低OOM,会同时打开上百文件
三、Sort-based Shuffle确定
1、如果Mapper中Task的数据量过大,依旧会产生很多小文件;此时在Shuffle传递数据的过程中到Reducer端,reduce会需要同时打开大量的记录来进行反序列化,导致大量的内存消耗和GC的巨大负担,造成系统缓慢甚至奔溃!
2、如果在分片内也需要排序的话,此时也需要进行Mapper端和Reducer端的两次排序!
博客:http://bolg.sina.com.cn/ilovepains
新浪微博:http://weibo.com.ilovepains/qq:1740415547
手机:18610086859
邮箱:18610086859@vip.126.com