MapReduce工作机制(二、Map端流程)

文章从《Hadoop权威指南》以及《Hadoop技术内幕:深入解析MapReduce架构设计与实现原理》中总结而来。


四种Map Task:
  1. Job-setup Task:作业运行时启动的第一个任务
  2. Job-cleanup Task:作业运行时启动的最后一个任务
  3. Task-cleanup Task:任务失败或是被杀死后用于清理已写入临时目录中数据的任务
  4. Map Task: 处理数据,输出结果存到本地磁盘

这里写图片描述

Read阶段

Maptask通过用户编写的RecordReader,从输入InputSplit中解析出一个个key、value

Map阶段

将解析出的key、value交给用户编写的map()函数处理,产生一系列新的key、value

Collect阶段

在用户编写的map()函数中,当数据处理完成后,一般会调用OutputCollector.collect()输出结果。

该函数内部会调用Partitioner产生key、value分片,并写入一个环形内存缓冲区中。

Spill阶段

当环形缓冲区满后,MapReduce会将数据写到本地磁盘上,生成一个临时文件。

数据写入磁盘之前,先要对数据进行一次本地排序,并在必要时对数据进行合并压缩等操作。

Combine阶段

对所有的临时文件进行合并,以确保最终只生成一个文件。

Collect

每个map任务都有一个环形缓冲区,用于收集Map结果,减少磁盘IO的影响。

Mapper走完业务逻辑之后,调用MapOutputCollector.collect()输出结果。
MapOutputCollector(接口类)有两个实现类:

  1. MapOutputBuffer,写入环形缓冲区
  2. DirectMapOutputCollector-没有ReduceTask时调用,直接写入HDFS

MapOutputBuffer采用二级索引结构,涉及三个环形内存缓冲区,由io.sort.mb=100M控制

1. kvoffsets-键值对索引的偏移量(在kvindice中进行查找)
  • 一对键值对只占用kvoffsets的一个int大小
  • 由三个变量控制,kvstart,kvend,kvindex
  • 一开始kvstart=kvend,kvindex指向待写入位置,当写入一条数据后,kvindex向后移动一位
  • 当kvoffsets使用率超过io.sort.spill.percent=80%后,数据开始溢出(spill)到磁盘
  • io.sort.record.percent控制索引与实际数据占用内存大小的比值

2. kvindices-分区信息,键值对索引(在kvbuffer中进行查找)
  • 一个键值对占用数组kvindices的3个int大小(分别保存partition号,key开始位置,value开始位置)
  • Hadoop按比例1:3将大小为io.sort.record.percent * io.sort.mb的内存空间分配给数组kvoffsets和kvindices。

3. kvbuffer-键值对具体的值
  • kvbuffer的读写操作由指针bufstart,bufend,bufindex,bufvoid,bufmark控制,前三者与kvoffsets中的作用相同。
  • bufvoid指向缓冲区中有效内存结束位置
  • bufindex会跟随key值和value值移动。
  • bufmark只会跟随value值移动。(bufmark≠bufindex时代表写入了key还没写入value)
  • kvbuffer使用率超过io.sort.spill.percent=80%后,数据开始溢出到磁盘。
  • 最多可以使用io.sort.mb的95%

这里写图片描述

单生产者消费者模型

生产者->MapOutputBuffer.collect(),MapOutputBuffer.Buffer.write()

消费者->SpillThread

//生产者部分主要伪代码
//取得下一个可写入的位置
spillLock.lock();
if(缓冲区使用率达到阈值){
    //唤醒SpillThread线程,将缓冲区数据写入磁盘
    spillReady.signal();
}
if(缓冲区满){
    //等待SpillThread线程结束
    spillDone.wait();
}
spillLock.lock();
//将数据写入缓冲区

Spill

  • 由SpillThread线程完成,是缓冲区kvbuffer的消费者
  • SpillThread调用函数sortAndSpill()将环形缓冲区kvbuffer的区间[bufstart,bufend)内数据写到磁盘上。
溢写过程

以kvbuffer为例,令bufend=bufindex将缓冲区[bufstart,bufend)(留了最后一个index)之间的数据写出到磁盘。

达到soft buffer limit时,将会启动spill线程,spill线程以bufstart为读指针向bufend移动对磁盘进行写入,此时Map Task可写入;

当达到hard buffer limit时,缓冲区将会阻塞直到spill线程执行完毕。完成后,MapTask才可以继续向kvbuffer写入数据。

最后bufstart到达bufend位置,等待新一轮溢写。

这里写图片描述

跨界写入的两次内存复制与三次内存复制没怎么看懂,先留着位置

这里写图片描述

Hadoop0.21对环形内存区的修改

  1. 不再将索引和记录分放到不同的环形缓冲区中,而是让它们共用一个环形缓冲区。
  2. 引入一个新指针equator,该指针界定了索引和数据的共同起始位置。从位置开始,索引和数据分别沿着相反的方向增长内存使用空间。(使得io.sort.record.percent可以被舍弃,减少溢写次数)
    这里写图片描述

MapTask端会产生一次溢写过程,Reduce端也会产生一次溢写过程。

sortAndSpill()
  1. 利用快速排序对缓冲区的区间内数据进行排序,先按分区编号partition排序,再按key排序(group by partition order by partition,key
  2. 按照分区编号从小到大将数据写入临时文件output/spillN.out(N表示当前溢写次数),如果设置了Combiner,则写入文件之前,还会对每个分区进行一次聚集操作。
  3. 将分区数据的元数据写到内存索引数据结构SpillRecord中,元数据包括每个分区在spillN.out中的偏移量,压缩前后数据大小。若索引大小超过1M,则写到output/spillN.out.index中。

Combine

  • 当所有数据处理完后,所有的临时文件会合并为一个大文件output/file.out同时生成索引文件output/file.out.index
  • 以分区为单位合并,对于每个分区,采用多轮递归合并。
多轮递归合并
  1. 合并io.sort.factor=100个文件,产生新文件
  2. 将新文件加入待合并列表中
  3. 对文件列表重新排序
  4. 重复上述步骤

这里写图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值