详细口述 MapReduce Shuffle 过程。
客套话
Shuffle 是 MapReduce 的核心,被称为奇迹发生的地方。分为 map 端与 reduce 端。
不过在官方文档中, Shuffle 仅仅指 reduce 端从 map 端拷贝数据的过程。既然大家都这么说,就大浪淘金,随波逐浪吧!
Map 端
在 map 端, map task 的每次处理分片中的一条记录。
task 输出的数据以及数据的索引首先会写到一块环形内存缓冲区里,缓冲区内一边是数据,一边是索引.
当缓冲区中的数据超过阈值的时候,缓冲区中的索引与数据就要开始溢出操作,而阈值可以通过 io.sort.spill.percent 来设置,默认 0.8,缓冲区的大小由 io.sort.mb 来设置,默认为100MB.这是由另一个线程来完成的。
线程先把溢出数据进行排序,实际上排的是索引,排序方式是先按照分区排序,然后按照 key 排序。
排完序后,如果有combiner,会先执行 combiner,然后按照索引的顺序依次将键值对写入溢出文件中,索引也会写道文件中,数据与索引使用文件名来匹配。
map task 执行结束,如果只有一个溢出文件,那这个文件就是 map 最终输出。如果有多个文件,就开始进行 merge 过程。
每次要 merge 的文件数使用 io.sort.factor 来设置,默认为10,超出这个数字会进行多轮 merge。
merger 的内部过程为两层 for 循环,外层循环是遍历分区,内层循环就是遍历每个溢出文件,将文件中的相应分区取出,组成一个段。在段内部,会先对键值对进行排序,如果有 combiner,并且溢出文件数大于 min.num.spill.for.combine 配置, 默认为3, 就会在进行 combiner 操作。每生成好一个段,就将该端放入最终文件中。直到所有的分区用上面的操作处理完成,map 端最终的文件也就完成了。
如果开启了中间输出压缩,会对文件进行压缩。配置再此:
关于压缩的配置
Reduce 端
- reduce 端会 copy map 输出中与自己对应的分区,是先 copy 到缓冲区内,缓冲区中的数据达到阈值时,就开始合并,阈值使用 mapred.job.shuffle.merge.percent 设置,默认为 0.66,缓冲区大小设置为 mapred.job.shuffle.input.buffer.percent, 是指占reduce task heap百分比。
- 合并的过程与 map 端的合并过程相同,是边 copy 边合并。合并完成后,就开始 reduce task ,生成输出文件。当然输出文件也可以被压缩。
shuffle 过程就结束了。
补充:
所有的排序使用 SortedCompareted Class 来设置的,如果没有,就使用 Key 类型自己实现的比较方法。
GroupingCompareted 总会在 combiner 和 reduce task 之前执行。
下面有个自己画流程图,里面 splii 写成了 pill,尴尬坏了。还有分片的计算方法也有错误,这就更尴尬的,不过已经在里面用文字指明了,嘿> MapReduce Shuffle 过程图解
参考博客:
[0] http://blog.youkuaiyun.com/jeremyxn/article/details/50839618
End!!