【发布时间】:2014-07-23 18:13:03
【问题描述】:
我正在阅读 Tom White 的 Hadoop: The definitive guide 3rd edtition。这是了解Hadoop 内部结构的绝佳资源,尤其是我感兴趣的Map-Reduce。
从书中,(第205页):
随机和排序
MapReduce 保证每个 reducer 的输入都是按键排序的。系统执行排序并将 map 输出作为输入传输到 reducer 的过程称为 shuffle。
由此推断,key在发送到reducer之前是先排序的,说明job的map阶段的输出是排序的。请注意:我不叫它mapper,因为map阶段包括mapper(程序员编写)和MR框架的内置排序机制。
地图一侧
每个映射任务都有一个循环内存缓冲区,它会将输出写入其中。缓冲区默认为 100 MB,可以通过更改 io.sort.mb 属性来调整大小。当缓冲区的内容达到某个阈值大小(io.sort.spill.percent,默认为 0.80 或 80%)时,后台线程将开始将内容溢出到磁盘。在溢出发生时,映射输出将继续写入缓冲区,但如果在此期间缓冲区填满,映射将阻塞,直到溢出完成。
在写入磁盘之前,线程首先将数据划分为 对应于 reducer 的分区,它们最终将被发送到这些分区。在每个分区内,后台线程按键执行内存排序,如果有组合器函数,则在排序的输出上运行。运行 combiner 函数可以得到更紧凑的 map 输出,因此写入本地磁盘和传输到 reducer 的数据更少。
我对上一段的理解是,在mapper产生key-value对的时候,key-value对是被分区和排序的。一个假设的例子:
考虑将 mapper-1 用于字数统计程序:
>mapper-1 contents
partition-1
xxxx: 2
yyyy: 3
partition-2
aaaa: 15
zzzz: 11
(注意with-in每个partition的数据都是key排序的,但是partition-1和partition-2的数据不一定要按顺序排列)
继续阅读本章:
每次内存缓冲区达到溢出阈值时,都会创建一个新的溢出文件,因此在映射任务写入其最后一条输出记录后,可能会有多个溢出文件。在任务完成之前,溢出文件被合并成一个单个分区和排序的输出文件。配置属性 io.sort.factor 控制一次合并的最大流数;默认值为 10。
我的理解是(请知道上面段落中的粗体词,它欺骗了我): 在一个映射任务中,多个文件可能会溢出到磁盘,但它们会合并到一个仍然包含分区并已排序的文件中。考虑与上面相同的示例:
在单个地图任务完成之前,它的中间数据可能是:
mapper-1 内容
spill 1: spill 2: spill 2:
partition-1 partition-1 partition-1
hhhh:5
xxxx: 2 xxxx: 3 mmmm: 2
yyyy: 3 yyyy: 7 yyyy: 9
partition-2 partition-2 partition-2
aaaa: 15 bbbb: 15 cccc: 15
zzzz: 10 zzzz: 15 zzzz: 13
map-task 完成后,mapper 的输出将是一个文件(注意现在添加了上面的三个溢出文件,但假设作业 conf 中没有指定组合器,则没有应用组合器):
>Mapper-1 contents:
partition-1:
hhhh: 5
mmmm: 2
xxxx: 2
xxxx: 3
yyyy: 3
yyyy: 7
yyyy: 9
partition-2:
aaaa: 15
bbbb: 15
cccc: 15
zzzz: 10
zzzz: 15
zzzz: 13
所以这里partition-1可能对应reducer-1。即上面partition-1段对应的数据发送到reducer-1,partition-2段对应的数据发送到reducer-2。
如果到目前为止,我的理解是正确的,
- 如何从映射器输出中获取包含分区和排序数据的中间文件。
- 有趣的是,单独运行 mapper 不会产生排序输出 与数据发送到reducer的点相矛盾的是没有排序的。 More details here
- 如果没有只运行 Mapper,即使没有应用组合器:More details here
【问题讨论】:
-
好问题和好答案!
标签: sorting hadoop mapreduce hadoop2