一、Client
1、根据用户配置的 split.min 和 split.max ,还有文件的 BlockSize 确定最终的 split 分片大小。
2、输出执行文件清单格式:
file | order | offset | size | locations | ||
---|---|---|---|---|---|---|
file1 | 1 | 0 | 4 | 1 | 3 | 9 |
2 | 4 | 4 | 2 | 5 | 7 | |
。。。 |
二、MapTask
1、准备阶段:
准备输入流,准备文件读取器 LineRecordReader 。从HDFS拉取文件, seek 到 split 的偏移位置,再向下一行(避开第一行,第一行有可能被切分),然后开始 for 循环每次读取文件的一行, 按 inputFormat 的要求做成 <k,v> 的格式。
2、map阶段:
每个 <k,v> 对执行一次 map 方法,同时给 <k,v> 分区,通过 newOutputCollector.write() 把 <k,v> 变成 <k,v,p> ,之后存进一个环形 buffer ,当环形 buffer 存到 80% 开始溢写, 溢写之前先 QuickSort (提前执行了一部分 reduce 的功能, 可以减少 reduce 时的磁盘i/o, 这也是整个框架体系中,唯一由乱序到有序的排序过程,其他地方的排序都是归并了),溢写之前还要看有没有 combiner ,有的话做 combiner (提前执行了一部分 reudce 的功能,可以数据量,从而减少网络I/O),之后就可以把这些数据溢写到磁盘上,最后在磁盘上按照不同分区做 merge,merge 成一个内部有序的大文件,等待 reduce 拉取。
三、ReduceTask
1、shuffle阶段:
拉取 Map 阶段的结果。
2、merge阶段:
一边拉取 Map 的结果,一边就进行 merge,merge 时根据配置的 Group Comparetor (没有的话用 Sort Comparetor, 也没有的话用 Key Comparetor),按 Key 进行分组, 直到 merge 结束。
3、reduce阶段:
reduce 时,每次针对一组数据进行 reduce 操作(但是这时并没有从磁盘取出这一组数据,只是获取到一个迭代器作为迭代的起始点),之后在 reduce 操作的内部,会使用刚获取的迭代器进行循环迭代,每次取出一条数据进行处理。如此反复循环直至 reduce 全部执行完毕。