MapReduce建立在HDFS基础上,为海量的数据提供了一个计算框架。由两个阶段组成:Map和Reduce,用户只需实现map()和reduce()两个函数,即可实现分布式计算。
1 架构
主从结构
1)JobTracker(主节点)
只有一个
- 接收客户提交的计算任务
- 任务调度:把计算任务分给 TaskTracker 执行
- 监控 TaskTracker 的执行情况
2)TaskTracker(从节点)
有很多个
- 执行 JobTracker 分配的计算任务(Map 映射,Reduce 归约)
2 运行流程
Map阶段的输入来自HDFS中的文件,一个map任务处理一个文件块。Map任务的输出会作为Reduce的输入,Reduce是对数据集进行精简,然后得出相应结果。
MapReduce的执行过程一般过程分为:Split, Map, Shuffle, Reduce, Output几个阶段
示例
hello java
hello c
hello php
hello javascript
hello java language
hello c language
hello php language
hello javascript language
1 Split阶段
文件分块。得益于HDFS的特性,文件在HDFS中是分块存储的。假设每个文件块包括2行内容的话,一共四个文件块:
文件块 | 内容 |
---|---|
0 | hello javahello c |
1 | hello phphello javascript |
2 | hello java languagehello c language |
3 | hello php languagehello javascript language |
2 Map阶段
每个块由一个map任务来处理,map函数将各个文件块的内容转换成新的key-value对
文件块 | 内容 | Map任务 | map函数输出 |
---|---|---|---|
0 | hello javahello c | Map任务0 | [hello:1][java:1][hello:1][c:1] |
1 | hello phphello javascript | Map任务1 | [hello:1][php:1][hello:1][javascript:1] |
2 | hello java languagehello c language | Map任务2 | [hello:1][java:1][language:1][hello:1][c:1][language:1] |
3 | hello php languagehello javascript language | Map任务3 | [hello:1][php:1][language:1][hello:1][javascript:1][language:1] |
3 Shuffle阶段
每个Mapper任务
- 分区:根据Reducer的任务数量对key-value对进行分区
- 排序:对每个分区的key进行排序
- 分组:对每个分区的key进行分组
- Combiner:执行Combiner
最后发送给Reducer任务
Map任务 | map函数输出 | 分区 | 排序 | 分组 | Combiner |
---|---|---|---|---|---|
Map任务0 | [hello:1][java:1][hello:1][c:1] | 分区0[hello:1],[java:1],[hello:1]分区1[c:1] | 分区0[hello:1],[hello:1],[java:1]分区1[c:1] | 分区0[hello:{1,1}],[java:1]分区1[c:1] | 分区0[hello:2],[java:1]分区1[c:1] |
Map任务1 | [hello:1][php:1][hello:1][javascript:1] | 分区0[hello:1],[php:1],[hello:1]分区1[javascript:1] | 分区0[hello:1],[hello:1],[php:1]分区1[javascript:1] | 分区0[hello:{1,1}],[php:1]分区1[javascript:1] | 分区0[hello:2],[php:1]分区1[javascript:1] |
Map任务2 | [hello:1][java:1][language:1][hello:1][c:1][language:1] | 分区0[hello:1],[java:1],[hello:1]分区1[language:1],[c:1],[language:1] | 分区0[hello:1],[hello:1],[java:1]分区1[c:1],[language:1],[language:1] | 分区0[hello:{1,1}],[java:1]分区1[c:1],[language:{1,1}] | 分区0[hello:2],[java:1]分区1[c:1],[language:2] |
Map任务3 | [hello:1][php:1][language:1][hello:1][javascript:1][language:1] | 分区0[hello:1],[php:1],[hello:1]分区1[language:1],[javascript:1],[language:1] | 分区0[hello:1],[hello:1],[php:1]分区1[javascript:1],[language:1],[language:1] | 分区0[hello:{1,1}],[php:1]分区1[javascript:1],[language:{1,1}] | 分区0[hello:2],[php:1]分区1[javascript:1],[language:2] |
4 Reduce阶段
Shuffle结束后,同一分区的数据会传送给同一个Reducer任务。
Reducer任务接收到key-value对后会先根据key进行排序和分组,最后执行Reducer函数输出结果。
Reduce任务 | 输入 | 排序 | 分组 | 输出 |
---|---|---|---|---|
Reduce任务0 | [hello:2][java:1][hello:2][php:1][hello:2][java:1][hello:2][php:1] | [hello:2][hello:2][hello:2][hello:2][java:1][java:1][php:1][php:1] | [hello:{2,2,2,2}][java:{1,1}][php:{1,1}] | [hello:8][java:2][php:2] |
Reduce任务1 | [c:1][javascript:1][c:1][language:2][javascript:1][language:2] | [c:1][c:1][javascript:1][javascript:1][language:2][language:2] | [c:{1,1}][javascript:{1,1}][language:{2,2}] | [c:2][javascript:2][language:4] |
Output阶段
输出格式会转换最终的键值对并写入文件。默认情况下键和值以tab分割,各记录以换行符分割。因此可以自定义更多输出格式,最终数据会写入HDFS。类似记录读取,自定义输出格式不在本书范围。