一、MapReduce 总体架构
整体的Shuffle过程包含以下几个部分:Map端Shuffle、Sort阶段、Reduce端Shuffle。即是说:Shuffle 过程横跨 map 和 reduce 两端,中间包含 sort 阶段,就是数据从 map task 输出到reduce task输入的这段过程。
-----------------------------------------------------------------------------------------------------------------------------
注意:sort、combine 是在 map 端的,combine 是提前的 reduce ,需要自己设置。
用户设置 combiner,combiner 就是根据相同 key 来合并结果的。MapReduce 的术语中,reduce 只指 reduce 端执行从多个 map task 取数据做计算的过程。除 reduce 外,非正式地合并数据只能算做 combine(比如这里 map 端的提前合并 combine)。MapReduce 中将 Combiner 等同于 Reducer。
---------------------------------------------------------------------------------------------------------------------------
官方给的文档中图为:其中分为 map 端 shuffle 和 reduce 端 shuffle ,这是总体的过程图,具体细节图例还需再在 map 和 reduce 两个阶段细分,详细图解见后文。这里先给出总体过程图:
Hadoop 集群中,大部分 map task 与 reduce task 的执行是在不同的节点上。当然很多情况下 Reduce 执行时需要跨节点去拉取其它节点上的map task结果。如果集群正在运行的 job 有很多,那么 task 的正常执行对集群内部的网络资源消耗会很严重。而对于必要的网络资源消耗,最终的目的就是最大化地减少不必要的消耗。还有在节点内,相比于内存,磁盘 IO 对 job 完成时间的影响也是可观的。从最基本的要求来说,对于 MapReduce 的 job 性能调优的 Shuffle 过程,目标期望可以有:
- 完整地从map task端拉取数据到reduce 端。
- 在跨节点拉取数据时,尽可能地减少对带宽的不必要消耗。
- 减少磁盘IO对task执行的影响。
总体来讲这段Shuffle过程,能优化的地方主要在于减少拉取数据的量及尽量使用内存而不是磁盘。
具体细分下来又分为Map端和Reduce端分别的Shuffle。
二、Map端Shuffle
实际包含了输入(input)过程、切分(partition)过程、溢写spill过程(sort和combine过程)、merge过程。
每个 map task 有一个内存缓冲区用于存储map的输出结果,缓冲区快满的时候需要将缓冲区的数据以一个临时文件的方式存放到磁盘,此过程称为“溢写”过程,当整个map task结束后再对此map task产生的所有临时文件做合并,生成最终的正式输出文件,然后等待reduce task来拉数据。
1.input过程。
在map task 执行时,其输入来源 HDFS的 block ,map task 只读取split 。Split 与 block 的对应关系可能是多对一,默认为一对一。
2.partition过程。
当前的 mapper的 part交给哪个 reduce的方法是:mapreduce 提供的Partitioner接口,对key 进行 hash 后,再以 reducetask 数量取模,然后到指定的 job 上。
缓冲区的作用是批量收集map结果,减少磁盘IO的影响。key/value对以及 Partition 的结果都会被写入缓冲区。写入之前,key 与value 值都会被序列化成字节数组。
3.sort & combine过程。
临时写入磁盘,然后重新利用这块缓冲区。这个从内存往磁盘写数据的过程被称为Spill,中文可译为溢写。
这个溢写是由另外单独线程来完成,不影响往缓冲区写map结果的线程。
溢写的比例spill.percent。这个比例默认是0.8,也就是当缓冲区的数据已经达到阈值(buffersize * spill percent = 100MB * 0.8 = 80MB),溢写线程启动,锁定这80MB的内存,执行溢写过程。Maptask的输出结果还可以往剩下的20MB内存中写,互不影响。
Combiner 将有相同key的 key/value 对加起来,减少溢写spill到磁盘的数据量。
Combiner的适用场景:由于Combiner的输出是Reducer的输入,Combiner绝不能改变最终的计算结果。故大多数情况下,combiner适用于输入输出的key/value类型完全一致,且不影响最终结果的场景(比如累加、最大值等……)。Combiner的使用一定得慎重,如果用好,它对job执行效率有帮助,反之会影响reduce的最终结果。
4.merge过程,产生spill file.
map 很大时,每次溢写会产生一个 spill_file,这样会有多个 spill_file,而最终的输出只有一个文件,在最终输出之前会对多个中间过程多次产生的溢写文件 spill_file 进行合并,此过程就是 merge。
把相同 key 的结果加起来。(当然,如果设置过combiner,也会使用combiner来合并相同的key)
三、Reduce端Shuffle
在 reduce task 之前,不断拉取当前 job 里每个 maptask 的最终结果,然后对从不同地方拉取过来的数据不断地做 merge ,也最终形成一个文件作为 reduce task 的输入文件。
1.copy过程,简单拉取数据。
本地磁盘中。
2.merge过程,和map端的merge类似。
Copy 过来的数据会先放入内存缓冲区中,这里的缓冲区大小要比 map 端的更为灵活,它基于 JVM 的 heap size 设置,因为 Shuffle 阶段 Reducer 不运行,所以应该把绝大部分的内存都给 Shuffle 用。这里需要强调的是,merge 有三种形式:1)内存到内存 2)内存到磁盘 3)磁盘到磁盘。默认情况下第一种形式不启用,让人比较困惑,是吧。当内存中的数据量到达一定阈值,就启动内存到磁盘的 merge 。与 map 端类似,这也是溢写的过程,这个过程中如果你设置有Combiner,也是会启用的,然后在磁盘中生成了众多的溢写文件。第二种merge方式一直在运行,直到没有 map 端的数据时才结束,然后启动第三种磁盘到磁盘的 merge 方式生成最终的那个文件。
3. reducer的输入文件。
磁盘中,但是需要将其放入内存中。当reducer 输入文件已定,整个 Shuffle 阶段才算结束。然后就是 Reducer 执行,把结果放到 HDFS 上。