Shuffle
In many ways,the shuffle is the heart of MapReduce and is where the ‘magic’ happens
Shuffle横跨Map、Reduce两个阶段
- Map阶段
-
进入Memory Buffer。每个
Map
都对应一个Memory Buffer
,每个map处理以后的结果都会进入环形缓存区。 -
Partition。对每一个<k,v>进行分区(打标签)。具体实现是根据key或value及reduce的数量来决定当前的这对输出数据最终应该交由哪个
reduce task
处理。默认取key的hash值后再以reduce task
数量取模。默认的取模方式只是为了平均reduce的处理能力,如果用户自己对Partitioner有需求,可以订制并设置到job上。有很多文章写得是spill以后,进行分区和排序。个人认为,数据在内存处理更快更方便,将处理以后的数据再存入硬盘,显然更合理。
-
Spill。当环形缓存区容量达到80%时(避免数据处理速度大于读写速度,造成数据丢失),就会开启单独的溢写进程, 将数据临时写入磁盘
-
Sort。溢写开始时,就会开始对数据根据key进行排序,且是对同一分区上的数据进行排序,即对序列化的字节排序。
-
combiner(可选)。当很多<k,v>需要发送到同一reduce端时,则他们被拼接到一起。比如<"aaa”,1>、
<“aaa”,1>
这样的键值对,我们就应该把它们的值合在一起,成为<“aaa”,2>。这个combiner是可以用户设置的,可以做累加、取最值等操作,它被用于减少溢写到磁盘的数据量,对map task
的中间结果进行优化,并将结果作为reduce task
的输出。 -
merge。当map task结束时,内存缓冲区内的文件即使不到80%,也会成为一个溢写文件。这里会将溢写文件归并到一起(减少输出),这个过程即为merge.在wordcount中,map task1的<“aaa”,5>,map task2的<“aaa”,1>会被merge成一个group,{“aaa”,[5,1,2,3…]},group中的值就是从不同溢写文件中读取出来的,在这里又会做一个combine,比如wordcount,这里又会将“aaa”的value合并(仍然没有计数)。
至此map端结束,reduce task不断地通过rpc从job tracker获取map task是否完成,如果获知某台tasktracker上的map task完成,shuffle的后半段即开始启动
- Reduce端
-
Copy。本质就是拉数据,通过
http
请求map
输出文件中,属于当前reduce
分区的数据。 -
Merge。把不同
map
端copy获取的值进行归并,copy
来的值先放内存中,与map端不同,这里的内存缓 存区更加灵活,(与jvm heap size有关,不做深究),但也是数据量达到一定阈值,就启动内存到磁盘merge。同样,如果设置了combiner,也是会启动。最终的数据默认以文件存在硬盘中,可以通过优化,存在内存中。
感谢下面两篇文章作者深入浅出的讲解