Lying Spark (4) - a text and understanding MapReduce Shuffle Spark Shuffle

Shuffle intended meaning, in the need to bring together MapReduce process on each node of the same type of data to a node calculation, the data gathered in different nodes together to become Shuffle process according to certain rules. 混洗, 洗牌

In the Hadoop MapReduce framework in, Shuffle as a bridge between the Map and Reduce, Map Reduce to use the data must go through this link Shuffle. Shuffle involved due to the transport network and disk read and write, so the level of performance Shuffle directly affects the performance and throughput of the entire process.

MapReduce in the Shuffle

 

This figure is the official network description of the process Shuffle, let's look at each end of the map and reduce end what has been done, how to do.

Map end

  1. When the map task execution, the block of input data from HDFS, MapReduce in concept, map reading only split the task. Correspondence between the split and the block may be many-to-default is one. 
  2. map before writing to disk, according to the final will be passed to the reduce the data into the appropriate partition (partition). Each partition, the background thread to sort button, if there combiner, which runs on the output sorted. ( combiner can map the results of a more compact, reducing disk data writing and data passed to reduce the [space-saving and IO] )
  3. When the map file is generated, it is not simply written to disk. It uses the buffer memory and writes data to the way in with the sorting efficiency considerations. (As shown in buffer in memory). Each map has an annular memory buffer for storing task output. the default buffer size is 100MB, once the threshold is reached (default 80%), a background thread began to overflow content (split) to disk. (If [during the split] in the meantime buffer is fill, map will be blocked until the process is completed writing to the disk. 
  4. 每次内存缓冲区达到阈值移出,就会新建一个溢出文件(split file)(上图 partition,sort and split to disk). 因此在map任务最后一个记录输出之后,任务完成之前会把一出的多个文件合并成一个已分区且已排序的输出文件.(上图 merge on task)

Reduce端

  1. map的输出文件在map运行的机器的本地磁盘(reduce一般不写本地), map的输出文件包括多个分区需要的数据, reduce的输入需要集群上多个map的输出. 每个map的完成时间可能不同, 因此只要有一个map任务完成, reduce就开始复制其输出.(上图 fetch阶段) reduce有少量复制线程(默认5个),因此能够并行取得map输出(带宽瓶颈).
  2. reduce如何知道从哪台机器获取map结果? map执行完会通知master, reduce总有一个线程定期轮询master(心跳)可以获得map输出的位置. master会等到所有reduce完成之后再通知map删除其输出.
  3. 如果map的输出很小,会被复制到reduce任务jvm的内存.否则map输出会被复制到磁盘(又写磁盘)
  4. 复制完所有map输出后,reduce任务进入排序合并阶段(其实是合并阶段,因为map的输出上有序的).这个维持顺序的合并过程是循环进行的.(比如50个map输出,合并因子是10(默认值), 则合并将进行5次, 每次合并10个文件, 最终有5个中间文件)
  5. 在最后reduce阶段,直接把数据输入reduce函数(上面的5个中间文件不会再合并成一个已排序的中间文件). 输出直接写到文件系统, 一般为HDFS. 

map输出为什么要排序?

  1. key存在combine操作,排序之后相同的key在一起方便合并.
  2. reduce按照key读数据时, 按照key的顺序去读, 遇到不一样的 key时即可知道之前的key的数据是否读取完毕. 如果没排序,则需要把全部数据都做处理. 

上面就是MapReduce的Shuffle过程, 其实Spark2.0之后的Shuffle过程与MapReduce的基本一致,都是基于排序的,早期spark版本中的shuffle是基于hash的,让我们来一起看下.

Spark中的Shuffle

Spark有两种Shuffle机制. 一种是基于Hash的Shuffle, 还有一种是基于Sort的Shuffle.在Shuffle机制转变的过程中, 主要的一个优化点就是产生的小文件个数.

 


以上图为例,在Spark的算子reduceByKey(_ + _, 2)产生的shuffle中,我们先看Shuffle Write阶段.

Shuffle Write (Hash-based)

 


如图所示, hash-based的Shuffle, 每个map会根据reduce的个数创建对应的bucket, 那么bucket的总数量是: M * R (map的个数 * reduce的个数).
(假如分别有1k个map和reduce,将产生1百万的小文件!)
如上图所示,2个core, 4个map task, 3个reduce task 产生了4*3 = 12个小文件.(每个文件中是不排序的)

Shuffle Write (Hash-based) 优化!

由于hash-based产生的小文件太多, 对文件系统的压力很大, 后来做了优化. 
把同一个core上的多个map输出到同一个文件. 这样文件数就变成了 core * R个.如下图:

 

2个core, 4个map task, 3个 reduce task, 产生了2*3 = 6个文件.
(每个文件中仍然不是排序的)

Shuffle Write (Sort-based)

由于优化后的hash-based Shuffle的文件数为: core * R, 产生的小文件仍然过大, 所以引入了 sort-based Shuffle

 


sort-based Shuffle中, 一个map task 输出一个文件.
文件在一些到磁盘之前, 会根据key进行排序. 排序后, 分批写入磁盘. task完成之后会将多次溢写的文件合并成一个文件. 由于一个task只对应一个磁盘文件, 因此还会单独写一份索引文件, 标识下游各个task的数据对应在文件中的起始和结束offset.

Shuffle Read

 


目前,hash-based 和 sort-based写方式公用相同的shuffle read. 
如下图所示: 

 

 


shuffle read task从多个map的输出文件中fetch自己需要的已排序好的数据. 
read task 会先从索引文件中获取自己需要的数据对应的索引, 在读文件的时候跳过对应的Block数据区, 只读当前自己这个task需要的数据. 

什么时候开始fetch数据?

当 parent stage 的所有ShuffleMapTasks结束后再fetch(这里和MapReduce不同). 理论上讲, 一个ShuffleMapTask结束后就可以fetch, 但是为了迎合 stage 的概念(即一个stage如果其parent stages没有执行完,自己是不能被提交执行的),还是选择全部ShuffleMapTasks执行完再去 etch.因为fetch来的 FileSegments要先在内存做缓冲(默认48MB缓冲界限), 所以一次fetch的 FileSegments总大小不能太大. 一个 softBuffer里面一般包含多个 FileSegment,但如果某个FileSegment特别大的话, 这一个就可以填满甚至超过 softBuffer 的界限.

边 fetch 边处理还是一次性 fetch 完再处理?

边 fetch 边处理.本质上,MapReduce shuffle阶段就是边fetch边使用 combine()进行处理,只是combine()处理的是部分数据. MapReduce为了让进入 reduce()的records有序, 必须等到全部数据都shuffle-sort后再开始 reduce(). 因为Spark不要求shuffle后的数据全局有序,因此没必要等到全部数据 shuffle完成后再处理. 
那么如何实现边shuffle边处理, 而且流入的records是无序的?答案是使用可以 aggregate 的数据结构, 比如 HashMap. 每从shuffle得到(从缓冲的 FileSegment中deserialize出来)一个 <key, value="">record, 直接将其放进 HashMap 里面.如果该HashMap已经存在相应的 Key. 那么直接进行 aggregate 也就是 func(hashMap.get(Key), Value).

Shuffle aggregate

shuffle read task拿到多个map产生的相同的key的数据后,需要对数据进行聚合,把相同key的数据放到一起,这个过程叫做aggregate.

 


大致过程如下图: 

 


task把读来的 records 被逐个 aggreagte 到 HashMap 中,等到所有 records 都进入 HashMap,就得到最后的处理结果。

fetch 来的数据存放到哪里?

刚 fetch 来的 FileSegment 存放在 softBuffer 缓冲区,经过处理后的数据放在内存 + 磁盘上。

小结:

其实MapReduce Suffle 和 Spark的Shuffle在主要方面还是基本一致的, 比如:都是基于sort的. 
细节上有一些区别, 比如 mapreduce完成一个map,就开始reduce, 而spark由于stage的概念,需要等所有ShuffleMap完成再ShuffleReduce.





Guess you like

Origin www.cnblogs.com/wangtcc/p/10936521.html