一、概述
MapReduce会确保每个
reducer
的输⼊都是按键排序的。从
map
⽅法输出数据开始、到作为输⼊数据传给reduce
⽅法的过程称为
shuffle
。在此,我们将学习
shuffle是如何⼯作的,因为它有助于我们理解⼯作机制(如果需要优化MapReduce
程序)。shuffle
属于不断被优化和改进的代码库的⼀部分,因此会随着版本的不同,细节上可能会发⽣变量。不管怎样,从许多⽅⾯来看,shuffle
是
MapReduce
的
“
⼼脏 “,是奇迹发⽣的地⽅。

二、环形缓冲区
每个map
任务都会有⼀个环形内存缓冲区⽤于存储
map
的输出数据。在默认情况下,缓冲区的⼤⼩为100MB,
这个值可以通过
mapreduce.task.io.sort.mb
属性来调整。⼀旦缓冲区的内容达到阙值(默认是0.8
,或者是
80%
,属性是mapreduce.map.sort.spill.percent),⼀个后台线程便开始把内容溢写
(spill)
到磁盘⾥,这个位置由属性mapreduce.cluster.local.dir
来指定的。在将数据溢写到磁盘过程中,map
的输出数据继续写到缓冲区,但如果在此期间缓冲区被填满,
map
会被阻塞直到写磁盘过程完成。
环形缓冲区:其实是⼀个字节数组kvbuffer. 有⼀个
sequator
标记,
kv
原始数据从左向右填充(
顺时针
)
,
kvmeta是对
kvbuffer
的⼀个封装,封装成了
int
数组,⽤于存储
kv
原始数据的对
应的元数据
valstart
,keystart,
partition
,
vallen
信息,从右向左
(
逆时针
)。

三、shuffle整体流程图
shuffle阶段是处于map阶段到reduce阶段的一个过程
a) 完整地从
map task
端拉取数据到
reduce
端。
b) 在跨节点拉取数据时,尽可能地减少对带宽的不必要消耗。
c) 减少磁盘
IO
对
task
执⾏的影响。
四、shuffle流程
1.
从
map
函数输出到
reduce
函数接受输⼊数据,这个过程称之为
shuffle.
2. map
函数的输出,存储环形缓冲区(默认⼤⼩
100M,
阈值
80M
)
3.
当达到阈值时,准备溢写到本地磁盘
(
因为是中间数据,因此没有必要存储在
HDFS上)
。在溢写前要进⾏对元数据分区
(partition)
整理,然后进⾏排序
(quick sort,通过元数据找到出key
,同⼀分区的所有
key
进⾏排序,排序完,元数据就已经有序了,在溢写时,按照元数据的顺序寻找原始数据进⾏溢写)
4.
如果有必要,可以在排序后,溢写前调⽤
combiner
函数进⾏运算,来达到减少数 据的⽬的
5.
溢写⽂件有可能产⽣多个,然后对这多个溢写⽂件进⾏再次合并
(
也要进⾏分区和排序)
。当溢写个数
>=3
时,可以再次调⽤
combiner
函数来减少数据。如果溢写个数
<3时,默认不会调⽤combiner
函数。
6.
合并的最终溢写⽂件可以使⽤压缩技术来达到节省磁盘空间和减少向
reduce
阶段传输数据的⽬的。(存储在本地磁盘中)
7. Reduce
阶段通过
HTTP
写抓取属于⾃⼰的分区的所有
map
的输出数据
(
默认线程数是5,因此可以并发抓取
)
。
8.
抓取到的数据存在内存中,如果数据量⼤,当达到本地内存的阈值时会进⾏溢写操作,在溢写前会进⾏合并和排序(
排序阶段
)
,然后写到磁盘中,
9.
溢写⽂件可能会产⽣多个,因此在进⼊
reduce
之前会再次合并
(
合并因⼦是
10),
最后⼀次合并要满⾜10
这个因⼦,同时输⼊给
reduce
函数,⽽不是产⽣合并⽂件。reduce函数输出数据会直接存储在
HDFS
上。