一.MapReduce
的思想核心是
分
而治之
, 充分利用了并行处理的优势。
- Mapper map()方法是对输入的一个KV对调用一次!!
- Reduce Reduce()方法是对相同K的一组KV对调用执行一次
- Drive
二.
MapReduce
原理分析
- MapTask运行机制详解:
1.
首先,读取数据组件
InputFormat
(默认
TextInputFormat
)会通过
getSplits
方法对输入目录中文件进行逻辑切片规划得到splits
,有多少个
split
就对应启动多少个
MapTask
。
split
与
block
的对应关系默认是一对一。
2.
将输入文件切分为
splits
之后,由
RecordReader
对象(默认
LineRecordReader
)进行读取,以
\n 作为分隔符,读取一行数据,返回<key
,
value>
。
Key
表示每行首字符偏移值,
value
表示这一行 文本内容。
3.
读取
split
返回
<key,value>
,进入用户自己继承的
Mapper
类中,执行用户重写的
map
函数,RecordReader读取一行这里调用一次。
4. map
逻辑完之后,将
map
的每条结果通过
context.write
进行
collect
数据收集。在
collect
中,会先对其进行分区处理,默认使用HashPartitioner
。
MapReduce
提供
Partitioner
接口,它的作用就是根据
key
或
value
及
reduce
的数量来决定当前的这对
输出数据最终应该交由哪个
reduce task
处理。默认对
key hash
后再以
reduce task
数量取模。默认的
取模方式只是为了平均
reduce
的处理能力,如果用户自己对
Partitioner
有需求,可以订制并设置到
job
上。
5.
接下来,会将数据写入内存,内存中这片区域叫做环形缓冲区,缓冲区的作用是批量收集
map
结果,减少磁盘IO
的影响。我们的
key/value
对以及
Partition
的结果都会被写入缓冲区。当然写入之前,key
与
value
值都会被序列化成字节数组。
环形缓冲区其实是一个数组,数组中存放着
key
、
value
的序列化数据和
key
、
value
的元数据信息,包括partition
、
key
的起始位置、
value
的起始位置以及
value
的长度。环形结构是一个抽象概念。
缓冲区是有大小限制,默认是100MB
。当
map task
的输出结果很多时,就可能会撑爆内存,所以需要在一定条件下将缓冲区中的数据临时写入磁盘,然后重新利用这块缓冲区。这个从内存往磁盘写数据的过程被称为Spill
,中文可译为溢写。这个溢写是由单独线程来完成,不影响往缓冲区写map结果的线程。溢写线程启动时不应该阻止
map
的结果输出,所以整个缓冲区有个溢写的比例spill.percent。这个比例默认是
0.8
,也就是当缓冲区的数据已经达到阈值(
buffer size * spillpercent = 100MB * 0.8 = 80MB),溢写线程启动,锁定这
80MB
的内存,执行溢写过程。
Maptask的输出结果还可以往剩下的
20MB
内存中写,互不影响。
6
、当溢写线程启动后,需要对这
80MB
空间内的
key
做排序
(Sort) <