内部流程
1.处理数据的输入路径
计算任务切片(mt个数),根据任务切片的个数启动mapTask.
2.MapTask
处理属于自己的任务数据(start(偏移量),length)
TextInputFormat就是数据切分,分成若干个split,以便确定MapTask个数,以及对应的split,且里面有一个createRecorder方法,返回一个LineRecordReader
一行一行的读(如果行数比较多,就会产生大量的kv)
offset--->K linecontent ---->v
map阶段读取的时候,虽然规定以128M结尾,但是不以标准的128M的字节就结束,假设是这样,万一刚好以在这个节点切开了摸一个单词,这样也会导致信息缺失,所以一般是在128M这个一行,以 \n 结束.
文件的个数至少是3个
- 举例
- 有300M
- 128M Task0 128~256 Task1 剩下的44M>128*0.1 就划分到第三个Task里面去, 如果 剩下的内存小于128*0.1 就会把这部分的内容划分到 前面两个Task里面去'
- 如果mapTask是这样划分的,如果是一个普通文本的,那么是按照文本个数和文本大小进行划分
2.1map阶段
当到达map阶段的时候,v.tostring() 把v里面每行的内容转成字符串,v.toString().split()进行切割,
- String[] ws = v.toString().split("\\s+")
- (w,1)(w,1)(w,1)(w,1)(w,1)(w,1)(w,1)
- 也是把这些kv封装在了Text IntWritable 里面,a,1,a,1b,1,c,1,f,1
- context.write(a,1)
- context.write(a,1)
- context.write(a,1)
- HashPartion里面的getPartion(){
- return(key.hashCode()&Integer.MAX_VALUE)%numReduceTasks} 注意并上一个Max_VALUE是为了取模的时候取到一个负数
- 产生大量的kv交给自定义的map类
- map(k,v ,context) 一个kv就会执行一次map方法,map里面有一个SequenceInputFormat可以读取各种类型的文件,这也一样会生产kv(Sequence是首位相连的kv,kv....)
context.write(NK,NV)----->MapOutPutBufffer (ctrl+ o进入里面的源码)
MapOutputBuffer是一个用来暂时存储map输出的缓冲区,它的缓冲区大小是有限的,当写入的数据超过缓冲区的设定的阀值时,需要将缓冲区的数据溢出写入到磁盘,这个过程称之为spill,spill的动作会通过Condition通知给SpillThread,由SpillThread完成具体的处理过程。如果缓冲区使用过的是简单的单向缓冲区,在一次写满后,flush到磁盘,那么在flush的过程中,将会严重影响到map向缓冲区写入的性能,因为在flush的时候,缓冲区是需要被锁定的。因此,MR采用了循环缓冲区,做到数据在spill的同时,仍然可以向剩余空间继续写入数据。
collect(k key,v value,final int partion) 在经过map里面进行输出的时候(a,1-1) 或者(b,1-0) 0,1是取模得到的分区标识.便于分区,分区的个数也是根据reduce个数决定的.得到经过分区标识的kv进入到圆形缓冲区,kv,he 元数据kvmeta,分别以不同方向进入缓冲区,kv写到80%就停止往里面写内容,要留一点空间给元数据
在环形数组里对k进行排序,当到达80%的时候,就会出现数据的溢出spill(数据溢出组件)溢出得救写入达到磁盘
- 输出的时候a,1a,1b,1b,1,f,1f,1.....这时候是区内排序,不是全局排序,也就是分区编号相同的才会进行一起排序因此得到的是
- a,1,a1c,1,c1,e,1,e,1(假设按照2取模)
- b,1,b,1,b1,d,1,d,1,
- 假设当前面80%的内容进行分区处理完后,还有剩下的,依然要根据之前的划分进行分区
- 这样就会差生多次溢出的区域内容,
a,1,a1c,1,c1,e,1,e,1|| b,1,b,1,b1,d,1,d,1,
- 然后输出 (TextOutFormat) 普通文本,因为开始读的是普通文本,
- 但是普通文本程序读起来很不方便,所以可以转化成首位相连的一窜kv输出流
- 称为SequenceFileOutputFormat
- Remote Procedure Calls 远程过程调用 (RPC) ,程序可使用这种协议向网络中的另一台计算机上的程序请求服务。
3reduce阶
因为有多个数据块,但是在不同的数据块根据一些规则进行分区的时候,会产相同的区号,这时候相同的区号就会通过shuffle 分配到同一reduce里面()重新再次合并(Merger),shuffle就像是一个重新洗牌分发牌的一个过程
当得到两个MapTask的两个同一个区号信息时,再次进行分组排序
- 里面会调用GroupingCompator 组件里面的方法compare(k1,k2)(判断两个相邻的key是否相同,如果相同存储在同一个等迭代器中交给同一个rudece方法处理)
- a----->Iterator<11111111>
- f------>Iterator<11111111>显示的是每类字母的个数
- 实现聚合操作 输出a 8 f 7 ........
- 在OuputFormat类,这个类里面有对应输出的路径,也是结果结果路径
- 这个结果路径可以说本地路径也可以是hdfs 路径