MapReduce确保每个reducer的输入都是按键排序的,系统执行排序,将map输出作为输入传给reducer的过程称为shuffle

map函数开始产生输出时,并不是简单的将它写到磁盘,这个过程更复杂,它利用缓冲的方式写到内存并出入效率的考虑进行预排序

每个map任务都有一个环形内存缓冲区用于存储任务输出,在默认情况下,缓冲区的大小为100MB,这个值可以改变mapreduce.task.io.sort.mb属性来调整,一旦缓存内容达到阈值,默认为80%,一个后台进程便开始把内容溢出(spill)到磁盘,在溢出写到磁盘过程中,map输出继续写到缓冲区,但如果在此期间缓冲区被填满,map会被阻塞直到写磁盘过程完成.溢写过程按轮询方式将缓冲区中的内容写到mapreduce.cluster.local.dir属性在作业特定子目录下指定的目录中.

reduce部分:map输出文件位于运行map任务的tasktracker的本地磁盘,现在,tasktracker需要为分区文件运行reduce任务,并且,reduce任务的完成时间可能不同,因此在每个任务完成时,reduce任务就开始复制其输出,这就是reduce任务的复制阶段,reduce任务有少量复制线程,因此能够并行取得map输出,默认值是5个线程,这个默认值可以修改设置mapreduce.reduce.shuffle.parallelcopies属性即可

reduce如何知道要从哪台机器取得map输出呢?
    map任务成功完成后,它们会使用心跳机制通知他们的application master,因此,对于指定作业,applicaton master知道map输出和主机位置之间的映射关系,reduce中的一个线程定期询问master以便获取map输出主机的位置,直到获得所有输出位置
    由于第一个reducer可能失败,因此主机并没有在第一个reducer检索到map输出时就立即从磁盘上删除它们,相反,主机会等待,直到application master告知它删除map输出,这是作业完成后执行的

如果map输出相当小,会被复制到reduce任务JVM的内存(缓冲区大小可以设置),否则,map输出被复制到磁盘,一旦内存缓冲区达到阈值大小(可以设置),则合并后溢出写到磁盘中,如果指定combiner,则在合并期间运行它以降低写入硬盘的数据量

随着磁盘上副本增多,后台线程会将它们合并为更大的,排好序的文件.这会为后面的合并节省一些时间,注意,为了合并,压缩的map输出(通过map任务)都必须在内存中被解压缩

复制完所有map输出后,reduce任务会进入排序阶段(更恰当的说话是合并阶段,因为排序是在map端进行的),这个阶段将合并map输出,维持其顺序排序,这是循环进行的,比如,如果有50个map输出,而合并因子是10(默认设置,由mapreduce.task.io.sort.factor属性设置,与map的合并类似),合并将进行5趟,每趟将10个文件合并为一个文件,因此最后有5个中间件

在最后阶段,即reduce阶段,直接把数据输入reduce函数,从而省略了一此磁盘往返行程,并没有将这5个文件合并成一个已排序的文件作为最后一趟,最后的合并可以来自内存和磁盘片段

每趟合并的文件数实际上和示例上展示有所不同,目标是合并最小数量的文件以便满足最后一趟的合并系数,因此如果有40个文件,我们不会在四趟中每趟合并10个文件,在最后一趟中,4个已合并的文件和余下的6个(未合并的)文件合计10个文件

注意,这并没有改变合并次数,它只是一个优化措施,目的是尽量减少写到磁盘的数据量,因为最后一趟总是直接合并到reduce