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