## MapReduce框架
![image-20201021160744505](https://gitee.com/chenglm/lmpics/raw/master/img/20201021160744.png)
## Block块、切片、MapTask的关系
1. BlockSize在hadoop2.x为128M
2. split切片为逻辑概念,默认SplitSize = BlockSize,也可以自行设置
3. 一个job在Map阶段的并行度由job提交时的切片数量决定
4. 切片时,针对每一个文件单独切片
5. 每一个split切片分配一个MapTask单独处理
## InputFormat
将文件转换为KV值
自定义InputFormat
```java
public class WholeInputFormat extends FileInputFormat<Text, BytesWritable> {
@Override
public RecordReader<Text, BytesWritable> createRecordReader(InputSplit split, TaskAttemptContext context) throws IOException, InterruptedException {
return new WholeFileRecordReader();
}
@Override
protected boolean isSplitable(JobContext context, Path filename) {
return false;
}
}
```
```java
public class WholeFileRecordReader extends RecordReader<Text, BytesWritable>{}
//Text为文件名
//BytesWritable为文件
```
## Map
映射,负责数据的过滤分法,将原始数据转化为键值对
## Shuffle
在Map方法之后,Reduce方法之前的过程为Shuffle。
为了让Reduce可以并行处理Map的结果,必须对Map的输出进行一定的排序与分割,然后再交给对应的Reduce,而这个将Map输出进行进一步整理并交给Reduce的过程就是Shuffle。
### Map Shuffle阶段:
1. OutputCollector将Mapper处理好的数据收集起来,向环形缓冲区中输入(一侧存放KV值,另一侧存放索引)。
2. 环形缓冲区的内存达到80%(默认)时,进行分区排序(只移动索引的相应位置),排序后溢写到磁盘上,最终生成一个分区有序的文件
1. 分区排序为二次排序,使用快排。先对区号进行排序,再对区号内部进行排序。
1. 区间排序根据key值的hash值进行排序
2. 区内排序根据key实现的类WritableCompatarable中的CompareTo方法排序
2. 另外20%内存,依然源源不断地接受数据
3. 在进行溢写的时候,可以进行Combiner(如<b,1><b,1>就会合并成<b,2>)
1. 在spill溢写过程中,为了判定某个partition在这个文件中存放的起始位置,有一个三元组记录某个partition对应的数据在这个文件中的索引:起始位置、原始数据长度、压缩之后的数据长度。一个partition对应一个三元组。一个spill文件拥有一个index索引文件
4. 生成多个溢写文件
5. 将多个溢写文件进行归并排序,可以进行Combiner
1. 在本地目录上进行扫描,获取spill文件和index文件
2. 从索引列表中查询这个partition对应的所有索引信息,每个对应一个段插入到segement列表中。
3. 对这个partition对应的所有的segment进行归并合并,目标是合并成一个segment。
4. 将多个segement合并为一个输出文件。
### Reduce Shuffle阶段
1. 所有MapTask完成后,启动ReduceTask,通过HTTP向各个MapTask拖取它所需要的数据。
2. 归并排序(可能使用了分组)
## GroupingCompatator
继承了WritableComparator类,能够对Map输入的数据进行分组
## Reduce
Reduce一次读取一组,进行处理
## OutputFormat
## 工作流程
![img](https://gitee.com/chenglm/lmpics/raw/master/img/20201021160026.png)
![img](https://gitee.com/chenglm/lmpics/raw/master/img/20201021160033.png)
1. 在客户端完成对文件的切片
2. 获取信息(Job.split切片的信息、jar包本身、Job.xml配置信息)
3. 提交信息给yarn看
4. 根据Job.split,计算出MapTask数量(一个切片,一个MapTask)
5. (以一个MapTask为例),MapTask生成InputFormat对象,调用RecorderReader(负责把切片切割成KV值)
6. KV值输入给了Mapper的map方法(MapTask拥有InputFormat、Mapper两个对象),处理完的KV值以序列化的方式,被OutputCollector收集
7. 序列化的kv值存放在环形缓存中。环形缓冲区一边存放kv值,另一边存放相应的索引。当内存占用80%的时候:(溢写过程,spill)将kv值分区有序写入磁盘;在空的内存中,一边存放kv值,另一边存放索引。
8. 在溢写过程中,要进行分区、排序(快排)(二次排序,先按照partition进行排序,然后在parttion内按照key进行排序)(分区:按照条件输出到不同文件中)(一个切片中的数据,有可能包含若干个分区的数据)
9. 【该过程前可以运行Combiner(本质时一个Reducer),如<b,1><b,1>就会合并成<b,2>】溢写过程生成的文件为spill文件(分区且区内有序)(spill文件保存在{mapred.local.dir}指定的目录中,Map任务结束后就会被删除)(缓存每满足80%,要进行一次溢写,于是生成了多个溢写文件(spill文件))
10. 【该过程前可以运行Combiner】将多个溢写文件通过归并排序合并成一个文件(分区且有序)(5~10步完成,MapTask便生成了最终文件)
11. Combiner合并
12. 所有MapTask完成后,启动相应的ReduceTask,并告知ReduceTask处理范围(数据分区)
13. ReduceTask从所有MapTask生成的文件中,下载对应分区的文件到本地磁盘
14. 合并文件(归并排序)
15. Reducer一次读取一组(分组规则可以自定义,GroupingCompatator<k, knext>)
16. 使用OutputFormat(默认TextOutputFormat)调用RecordWriter输出
参考:
[尚硅谷](http://www.atguigu/com)
[MapReduce shuffle过程详解](https://blog.csdn.net/u014374284/article/details/49205885)