最近再准备着面试,就回顾了一下mr程序内部处理数据的流程。顺便总结一下
有写的不合理的地方,请大家多多包涵,并帮我指出
(以读取文本文件为例,前两步不同的实现类所拿到的数据结构不同)
1、程序被提交后mrappmaster会给它们分配任务,告知mapTask所要处理的文件切片
2、mapTask程序开始运行,mapTask会通过调用TextInputFormat这个类的createRecordReader()
方法得到一个LineRecordReader实例对象。
3、然后通过该实例对象反复去读取文件内容分别调用然后通过该实例对象反复
调用nextKey()和nextValue()方法,获取到每一行数据在该切片文件的偏移
量和内容
4、然后将取到的key,value交给自定义的XXMapper 中的map方法,处理数据
5、map阶段处理完后,通过context.write(K,V)将map阶段处理的结果以(K,V)
形式交给OutputCollector组件
6、由OutputCollector这个组件将拿到的(K,V)序列化后写入环形缓冲区中
缓存起来并记录角标位置(环形缓冲区容器是一个字节数组)
7、环形缓冲区会预留出空间,当环形缓冲区的容量到达一定程度时,会触发
一个spiller线程。(假如没到环形缓冲区容量的阈值文件切片就已经处理
完,也会进行一次溢出。)
8、该线程会通过记录的角标位置拿到这些数据,并调用默认的hashpartition
(如果自定义里分区器,则会调用自定义的分区器)对其进行分区,并对每
个分区的数据进行排序(快速排序算法,对Key进行排序)
9、然后将分好区排好序的数据进行溢出,写入该程序所在的本地磁盘文件中
(该文件是临时的)
10、然后通过Merger组件将数据溢出所产生的文件通过归并排序进行合并,相同
分区的数据会在一块生成新的文件(附带索引文件,记录每个分区的偏移量)
11、该mapTask程序退出
12、直到所有的mapTask完成后,开始运行reduceTask。
13、此时,nodeManager会开启一个名为mapreduce_shuffle的服务(实质上是
一个HTTP下载服务)
14、reduceTask通过Fetcher HTTP客户端去mapreduce_shuffle服务中依据索引
文件中分区数据的偏移量拉取对应分区的数据,发生数据的shuffle(每一
个reduceTask都会有一个编号,编号是多少就负责该编号对应分区的数据。
有几个reduceTask就会有几个分区)
15、下载下来的数据在该reduceTask程序所在的机器上,以文件形式存在。这
时reduceTask再通过Merger对分区文件做归并排序合成一个文件(SequenceFile)
16、调用XGroupingComparator这个类的compare方法,通过key对数据进行分组,
然后以key和迭代器的形式将数据反序列化交给XXReducer的reuduce()方法
17、reduce()方法一次处理一组的数据,处理完成后将结果以(K,V)形式交给
TextOutputFormat,调用getRecordWriter()方法得到LineRecordWriter实
例对象,并调用该对象的write()方法将结果写出(先写Key,然后写k和v的
分割符,然后写value,最后写一个换行符)
(若想要进行程序的优化,可以在将数据发送给reduceTask之前通过combiner组件做一次局部聚合)