• 什么是shuffle:mapreduce确保每个reduce的输入都是按键排序的,系统执行排序、将map输出作为输入传给reducer的过程称为shuffle。
  • 总结自7.3节:shuffle和排序。P195-P199

零. 引

总体逻辑图

mapreduce自定义类型 排序 mapreduce的shuffle过程排序_自定义

一. Map端

1. map溢写

hadoop map与reduce任务之间的中间结果为磁盘存储,但map任务并不是一开始即把输出写入文件。map任务有一个环形缓冲区用于存储任务输出,默认大小为100M1,若输出达到阙值(默认80%2),则会有一个后台进程白内存溢写到磁盘中。 文件位置由mapreduce.cluster.local.dir属性定义,一般位于本地磁盘中。每次溢写操作会生成一个溢写文件。
特殊情况:若在溢写过程中,剩余20%内存被填满时,map会被阻塞。

2. map分区、排序、合并

map任务在输出到内存后,会根据一定规则进行分区3,然后针对该分区按键排序。此任务由一个后台线程完成。任务完成之前溢出文件会被合并为一个已分区且已排序的文件。

3. map combiner

3.1 combiner函数说明

combiner函数:hadoop 的一个针对map任务输出的优化函数,其结果会作为reduce任务输入。并不是所有任务都适合于该函数,一般该函数用于计算最大值,最小值等(功能满足结合律)功能。由于该函数属于优化函数,其可能被调用0次到多次。其会使map输出更加紧凑。其本质上相当于一个本地reduce操作。

3.2 combiner函数作用时间

combiner函数指定之后,hadoop会在以下情况下调用该函数。

  • 达到内存溢出阙值,写入磁盘之前。
  • 溢出文件个数大于3个4,在合并前,会再次运行。

二. Reduce端

1. reduce任务的开始时间

reduce任务并不是等所有的map任务完成后才开始运行,reduce任务具有复制线程,在每个map任务完成后,复制线程便会复制其输出。map任务全部完成且数据取完后,才开始进行reduce后续操作(复制完成前为复制阶段)。

2. reduce如何获取map端输出文件

map任务完成后,会使用心跳通知AM(所以AM知道map任务与node的对应关系),reduce有一个线程定期询问AM以确定map位置,直到获取所有文件。 reduce默认有5个5复制线程用于复制文件。

3. map输出存储

若map输出很小,其会被复制到reduce任务的JVM内存6中,否则会被复制到磁盘中。一旦内存缓冲区达到阙值7或达到map输出阙值8则合并后溢出写入到磁盘中(斜体部分为hadoop权威指南原话,求指导)。若制定了combiner,则在合并期间运行它以降低写入硬盘的数据量。磁盘中副本量变大时,后台线程会将他们合并成更大的,排好序的文件。map压缩的输出数据会在内存中解压。

4. reduce的排序(或叫合并、归并)阶段

4.1 开始时间

reduce获取到所有map输出后即进入此阶段。

4.2 主要任务

循环合并map输出,维持其顺序排序。生成reduce最后一趟所需文件。

4.3 合并因子

合并因子,即每次合并的文件数,默认为10,通过mapreduce.task.io.sort.factor属性设置。假设有30个map输出,合并因子为10,则其需要合并3趟。
合并时有个原则,即尽量减少磁盘IO。因此一般情况下,合并的目标是合并最少数量文件以满足最后一趟的合并系数。以如下为例:

有30个map输出,合并因子为10,则在排序阶段完成后结果为1, 1, 1 ,7,最后7个文件未合并。三趟合并完后,共剩余10个文件以满足最后一趟合并因子
3			------>     1  第一趟
10		------>     1  第二趟
10		------>     1  第三趟
7			------>     7  未合并

5. reduce 函数阶段

reduce直接对已排序输出中的每个键调用reduce函数,此阶段的输出直接写入到输出文件系统中。如果采用HDFS,由于节点NM也运行data node,因此第一个块副本被写到本地磁盘。

注意事项(特别重要):

groupComparator与partiton的主要用途区别:

  • partiton是为了确定map任务的指定键对应的分区属于哪个reduce task。
  • groupComparator是为了在一个reduce task下区分key的聚合。
    举个栗子,在map阶段输出结果为1,一,2,二。设置reducetask数量为2,名字为r1,r2。此时想把1,一,交给r1处理。2,二,交给r2处理。就需要自定义partiton通过返回值来完成。但是1,一虽然进入了r1。但是r1,并不认为两者是相同的,也就是在输出的结果上并没有放在一行,而是两行。如果想让输出结果放到一行,就需要重新定义groupComparator组件。
    ps:一个reduce task会输出一个文件。一个reduce阶段的key对应文件中的一行

  1. 属性由mapreduce.task.io.sort.mb设置 ↩︎
  2. 属性由mapreduce.map.sort.spill.percent设置,值为0.8或80% ↩︎
  3. 对于map输出的每一个键值对,系统都会给定一个partition,partition值默认通过计算key的hash值后对Reduce task的数量取模获得。分区方式可自定义,详见链接 ↩︎
  4. 属性由mapreduce.map.combine.minspills设置,默认为3 ↩︎
  5. 属性由mapreduce.reduce.shuffle.parallelcopies设置。 ↩︎
  6. 属性由mapreduce.reduce.shuffle.input.buffer.percent设置。 ↩︎
  7. 属性由mapreduce.reduce.shuffle.merge.percent设置。 ↩︎
  8. 属性由mapreduce.reduce.merge.inmem.threshold设置。 ↩︎