Spark shuffle详细过程

有许多场景下,咱们须要进行跨服务器的数据整合,好比两个表之间,经过Id进行join操做,你必须确保全部具备相同id的数据整合到相同的块文件中。那么咱们先说一下mapreduce的shuffle过程。算法

 

Mapreduce的shuffle的计算过程是在executor中划分mapper与reducer。Spark的Shuffling中有两个重要的压缩参数。spark.shuffle.compress true---是否将会将shuffle中outputs的过程进行压缩。将spark.io.compression.codec编码器设置为压缩数据,默认是true.同时,经过spark.shuffle.manager 来设置shuffle时的排序算法,有hash,sort,tungsten-sort。(用hash会快一点,我不须要排序啊~)数组

 

Hash Shuffle缓存

使用hash散列有不少缺点,主要是由于每一个Map task都会为每一个reduce生成一份文件,因此最后就会有M * R个文件数量。那么若是在比较多的Map和Reduce的状况下就会出问题,输出缓冲区的大小,系统中打开文件的数量,建立和删除全部这些文件的速度都会受到影响。以下图:服务器

 

这里有一个优化的参数spark.shuffle.consolidateFiles,默认为false,当设置成true时,会对mapper output时的文件进行合并。若是你集群有E个executors(“-num-excutors”)以及C个cores("-executor-cores”),以及每一个task又T个CPUs(“spark.task.cpus”),那么总共的execution的slot在集群上的个数就是E * C / T(也就是executor个数×CORE的数量/CPU个数)个,那么shuffle过程当中所建立的文件就为E * C / T * R(也就是executor个数 × core的个数/CPU个数×reduce个数)个。外文文献写的太公式化,那么我用通俗易懂的形式阐述下。就比如总共的并行度是20(5个executor,4个task)  Map阶段会将数据写入磁盘,当它完成时,他将会以reduce的个数来生成文件数。那么每一个executor就只会计算core的数量/cpu个数的tasks.若是task数量大于总共集群并行度,那么将开启下一轮,轮询执行。app

速度较快,由于没有再对中间结果进行排序,减小了reduce打开文件时的性能消耗。性能

固然,当数据是通过序列化以及压缩的。当从新读取文件,数据将进行解压缩与反序列化,这里reduce端数据的拉取有个参数spark.reducer.maxSizeInFlight(默认为48MB),它将决定每次数据从远程的executors中拉取大小。这个拉取过程是由5个并行的request,从不一样的executor中拉取过来,从而提高了fetch的效率。 若是你加大了这个参数,那么reducers将会请求更多的文数据进来,它将提升性能,可是也会增长reduce时的内存开销fetch

 

 

Sort Shuffle优化

Sort Shuffle如同hash shuffle map写入磁盘,reduce拉取数据的一个性质,当在进行sort shuffle时,总共的reducers要小于spark.shuffle.sort.bypassMergeThrshold(默认为200),将会执行回退计划,使用hash将数据写入单独的文件中,而后将这些小文件汇集到一个文件中,从而加快了效率。(实现自BypassMergeSortShuffleWriter中)编码

那么它的实现逻辑是在reducer端合并mappers的输出结果。Spark在reduce端的排序是用了TimSort,它就是在reduce前,提早用算法进行了排序。  那么用算法的思想来讲,合并的M N个元素进行排序,那么其复杂度为O(MNlogM) 具体算法不讲了~要慢慢看~spa

随之,当你没有足够的内存保存map的输出结果时,在溢出前,会将它们disk到磁盘,那么缓存到内存的大小即是 spark.shuffle.memoryFraction * spark.shuffle.safyFraction.默认的状况下是”JVM Heap Size * 0.2 * 0.8 = JVM Heap Size * 0.16”。须要注意的是,当你多个线程同时在一个executor中运行时(spark.executor.cores/spark.task.cpus 大于1的状况下),那么map output的每一个task将会拥有 “JVM Heap Size * spark.shuffle.memoryFraction * spark.shuffle.safetyFraction / spark.executor.cores * spark.task.cpus。运行原理以下图:

使用此种模式,会比使用hashing要慢一点,可经过bypassMergeThreshold找到集群的最快平衡点。

 

Tungsten Sort

使用此种排序方法的优势在于,操做的二进制数据不须要进行反序列化。它使用 sun.misc.Unsafe模式进行直接数据的复制,由于没有反序列化,因此直接是个字节数组。同时,它使用特殊的高效缓存器ShuffleExtemalSorter压记录与指针以及排序的分区id.只用了8 Bytes的空间的排序数组。这将会比使用CPU缓存要效率。

每一个spill的数据、指针进行排序,输出到一个索引文件中。随后将这些partitions再次合并到一个输出文件中。

 

本文翻译自一位国外大神的博客:https://0x0fff.com/spark-memory-management/

相关文章
相关标签/搜索