文章目录
- 引子
- Map端的Shuffle过程
- 分区数据
- 排序数据
- 归并文件
- Reduce端的Shuffle过程
- 领取数据
- 归并数据和文件
引子
虽然我们编写Mapreduce程序只需着眼于编写Map端的map函数和Reduce端的reduce函数,但Shuffle过程是Mapreduce工作流程的核心环节,理解Shuffle过程是我们理解Mapreduce工作流程的核心关键。
Mapreduce的简要工作流程可看下图
从图中我们可以看出Mapreduce的工作流程分为Map、Shuffle、和Reduce,其中Shuffle过程横跨Map端和Reduce端,而Map端的map任务和Reduce端的reduce任务不包含在Shuffle过程中。
其中关于Shuffle过程的详细流程,或许下图会描绘得更加详细,下面针对下图进行展开说明
Map端的Shuffle过程
在Map端,经过map任务处理后的数据——一系列<k,v>键值对,首先进入缓存,当缓存的数据到达一定容量(缓存空间的溢写比)的时候,就会启动Map端的溢写过程,把map任务的输出结果溢写到磁盘里,在溢写到磁盘之前,会经历Map端的Shuffle处理阶段——分区、排序、合并(可选,需要自己定义,为了简述最简化,这里不唠),在溢写到磁盘之后,多个溢写文件会经历归并过程。
值得一提的是,Mapreduce的输入和输出都是保存到分布式文件系统中的,而中间结果是保存到本地磁盘中的(而这一点也成为了Mapreduce速度上的鸡肋),所以Shuffle过程会涉及大量的本地磁盘操作。
分区数据
缓存中每个经由map任务处理后的输出键值对<k,v>中的key被哈希函数哈希后再用reduce任务的数量进行取模,这样就把一系列<k,y>键值对分成多个区(分区数目对应reduce任务数量),每个区的数据交给对应的reduce(一个溢写文件的一个区对应一个reduce任务)去处理,实现并行计算。
排序数据
经过分区后的数据,对于每个分区的所有键值对,会按照key进行排序。
缓存数据经过分区和排序后,就会被溢写到磁盘,每一次溢写都会被生成一个溢写文件,并清空缓存中相应的数据。
归并文件
每一次溢写都会被生成一个溢写文件,这样随着map任务的不断执行,溢写的文件会在磁盘内越堆越多,由此,在map任务完成后,会对磁盘中的溢写文件执行文件归并,把多个溢写文件归并成一个大的溢写文件
Reduce端的Shuffle过程
领取数据
当所有的map任务都完成之后,Reduce端会收到通知去Map端上把属于自己处理的分区数据领取回来,由于存在多个Map端,自然Reduce端会分出多个线程去不同的Map端上领取数据。
归并数据和文件
当所有数据领取回来之后,会先放到缓存里,当缓存的数据满了之后,再溢写到磁盘上,在溢写到磁盘之前也会对数据进行一遍归并操作,即同样key的数据会被归并生成一个溢写文件,这样随着Reduce拉取回来的数据不断增多,缓冲区满了一遍又一遍,自然会生成多个溢写文件。
当Map端的数据全部被领回来的时候,磁盘中的溢写文件也会被归并成大的溢写文件。把磁盘上的多个溢写文件归并成大的溢写文件,可能需要执行多轮归并,每轮归并操作可以归并的文件数量是由参数io.sort.factor的值控制的(默认是10),假设磁盘产生了50个溢写文件,每轮可以归并10个溢写文件,则需要经过5轮归并,生成5个溢写文件。
磁盘中经过多轮归并生成的若干个文件不会被继续归并成一个大文件,而是在内存中进行归并,归并完毕后输入给reduce任务,这样可以减少磁盘读写开销。
需要说明的是在多轮归并中,每轮归并所归并的文件数选取策略会尽可能地使最后一轮的归并文件数足够io.sort.factor的值。因此如果有40个文件,我们并不会在四趟中每趟合并10个文件从而得到4个文件。相反,第一趟只合并4个文件,随后的三趟合并完整的10个文件。在最后一趟中,4个已合并的文件和余下的6个(未合并的)文件合计10个。