Spark Sort-Based Shuffle内幕彻底解密(DT大数据梦工厂)

内容:

1、为什么使用Sorted-Based Shuffle;

2、Sorted-Based Shuffle实战;

3、Sorted-Based Shuffle内幕;

4、Sorted-Based Shuffle的不足;

最常用的Shuffle方式,Sorted-Based Shuffle涉及了大规模Spark开发、运维时核心问题,以及答案的要害所在。

必须掌握这一讲内容。

本课是从Spark初级人才成功升级为Spark中级人才的通道。

稍有水平的大公司,面试内容本讲肯定会涉及。

==========为什么需要Sorted-Based Shuffle ============

1、Spark开始是Hash-Based Shuffle,Shuffle一般包含两阶段的任务:产生Shuffle数据的阶段(Map阶段,额外补充,需要实现ShuffleManager中的getWriter来写数据(数据可以 通过BlockManager写在Memry、Disk、Tachyon等,例如想非常快的Shuffle,此时考虑可以把数据写在内存中,但是内存不稳定,建议采用MemoeyAndDisk方式,更保险可以MemoeyAndDisk 2方式,但是开销也大)、使用Shuffle数据的阶段(Reduce阶段,额外补充,需要实现ShuffleManager的getReader,Reader会向Driver去获取上一个Stage产生的Shuffle数据)

2、Spark是链式表达式,可以在一个链条上有很多Mapper和很多Reducer。

如果不止一个Stage,Spark除了最后的Stage是Reducer之外,开始Stage是整个Job的Mapper,中间的每个Stage即是Mapper又是Reducer,它是上一个Stage的Reducer,下一个Stage的Mapper。

如果只有一个Stage,则这个Job就相当于只有一个Mapper阶段,不会产生Shuffle,适合于简单的ETL。

3、Spark Shuffle在最开始的时候,只支持Hash-Based Shuffle,而Hash-Based Shuffle默认Mapper阶段会为Reducer阶段的每一个Task,单独创建一个文件来保存该task中要使用的数据,但是在一些情况下(例如数据量非常大的情况)会造成大量文件(M*R,M代表Mapper中的所有的并行任务数量,R代表Reducer中所有的并行任务数量)的随机磁盘IO操作,且会形成大量的memory消耗,极易造成OOM,这是致命的问题,第一不能处理大规模的数据,第二Spark不能运行在大规模的分布式集群上!后来的方案是加入了Shuffle Consolidate机制来将Shuffle时候产生的文件数量减少到C*R个(C代表在Mapper端同时能够使用的cores的数量,R代表Reducer中所有的并行任务数量),但是此时如果Reducer端的并行数据分片(或者任务)过多的话,则C*R可能依旧过大,此时依旧没有逃脱文件打开过多的厄运;

Spark(Spark 1.1版本之前)在引入Sort-Based Shuffle之前,比较适用于中小规模的大数据处理。

4、为了让Spark在更大规模的集群上更高性能的处理更大规模的数据,于是就引入了Sort-Based Shuffle(Spark1 1.1版本开始),Spark可以胜任任意规模(包含PB级别及PB以上的级别)大数据的处理,尤其是随着钨丝计划的引入和优化,把Spark更快速的在更大规模的集群处理更海量的数据的能力推向了一个巅峰!

想到了Hadoop的Map Reduce Shuffle,它是排序的。有环形内存缓存区,既有数据由有索引。

5、Spark 1.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.toLowerCaseshuffleMgrName)
val shuffleManager = instantiateClass[ShuffleManager](shuffleMgrClass)

实现Shuffle Manager接口可以根据自己的业务实际需要去优化的使用自定义的Shuffle实现;

6、Spark1.6默认采用的是Sort-Based Shuffle的方式,见第5点Spark的源代码,也同时说明Spark-defalut

conf文件中可以配置spark.shuffle.manager来设置Shuffle Manager的实现

Sort-Based Shuffle不会为每个Reducer中的Task生成一个单独的文件,相反,Sort-Based Shuffle 会把Mapper中每个ShuffleMapTask所有的输出数据只写入一个文件中,因为每个ShuffleMapTask中的数据会被分类,所以Sort-Based Shuffle 使用了index文件存储具体ShuffleMapTask输出数据在同一个Data中是如何进行分类的信息(数据要分类,才要Shuffle,Shuffle之后还要聚类)。所以说Sort-Based Shuffle会在Mapper中的每一个Shuffle Map Task中产生两个文件,其中Data是存储Shuffle当前Task的Shuffle输出的,Index文件中存储数据了Data文件中的数据通过Partitioner的分类信息。此时下一个阶段的Stage中的Task就是根据这个Index文件获取自己所要抓取的上一个Stage中的Shuffle Map Task产生的数据的。

Sort-Based Shuffle产生的临时文件的数量正确的答案是2M(M代表Mapper中并行partition的总数量,其实就是Mapper端Task的总数量,这和实际并行的数量是两码事)

回顾整个Shuffle的历史,Shuffle产生的临时文件的数量的变化为依次为:

Basic Hash Shuffle M*R;

Consalisate方式Hash Shuffle C*R;

Sort-Based Shuffle 2M;

==========在集群中动手实战Sort-Based Shuffle============

自己先建个目录,上传几个文件

root@Master:/usr/local/hadoop-2.6.0/sbin# hadoop dfs -mkdir /library

DEPRECATED: Use of this script to execute hdfs command is deprecated.

Instead use the hdfs command for it.

16/02/13 13:18:41 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable

root@Master:/usr/local/hadoop-2.6.0/sbin# hadoop dfs -mkdir /library/dataforSortedShuffle

DEPRECATED: Use of this script to execute hdfs command is deprecated.

Instead use the hdfs command for it.

16/02/13 13:19:27 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable

root@Master:/usr/local/hadoop-2.6.0# hadoop dfs -put LICENSE.txt /library/dataforSortedShuffle/

DEPRECATED: Use of this script to execute hdfs command is deprecated.

Instead use the hdfs command for it.

16/02/13 13:21:06 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable

./spark-shell --master spark://Master:7077,Worker1:7077,Worker2:7077

sc.textFile("/library/dataforSortedShuffle").flatMap(_.split(" ")).map(word=>(word,1)).reduceByKey(_+_).saveAsTextFile("/library/dataoutput1")

spacer.gif

并行度6

临时数据文件和索引文件,2个数据2个索引:

spacer.gif

spacer.gif

spacer.gif

集群的三台机器全部有2个索引2个数据,一共是6个数据6个索引的临时文件,所以就如上面理论所说的,说明默认的是sort的方式产生的 ,并行度为6的时候,会产生6*2个临时文件,2M个文件!!!

命名规则

shuffle_0_0_0.data

shuffle_0_1_0.data

shuffle_0_2_0.data

等等

shuffle_0_0_0.index

shuffle_0_1_0.index

shuffle_0_2_0.index

等等

过一会,临时文件就被删除了。。

在Sort-Based Shuffle中,Reducer是怎么样获取自己需要的数据的呢?

具体而言,Reducer首先找Drvier去获取父Stage中每个Shuffle Map Task输出的位置信息,根据位置信息获取index文件,解析index文件,从解析的index文件中获取index文件中获取Data文件中属于自己的那部分内容。

默认Sort-Based Shuffle的几个缺陷:

1、如果Mapper中Task的数量过大,依旧会产生很多小文件;此时在Shuffle传递数据的过程中到Reducer端,Reduce会需要同时打开大量的记录来进行反序列化,导致大量的内存消耗,和gc巨大负担,造成系统缓慢甚至崩溃;

2、如果需要在分片内也进行排序的话,此时需要Mapper端和Reducer端的两次排序

王家林老师名片:

中国Spark第一人

新浪微博:http://weibo.com/ilovepains

微信公众号:DT_Spark

博客:http://blog.sina.com.cn/ilovepains

手机:18610086859

QQ:1740415547

邮箱:[email protected]


本文出自 “一枝花傲寒” 博客,谢绝转载!

你可能感兴趣的:(spark,sort-based,Shuffle内幕彻底解密)