hadoop mapreduce 过程详解
1:最简单的过程:
map - reduce
2:定制了partitioner以将map的结果送往指定reducer的过程:
map - partition - reduce
3:增加了在本地先进性一次reduce(优化)
map - combin(本地reduce) - partition - reduce
基本上,一个完整的mapreduce过程可以分为以上3中提到的4个步骤,下面以hadoop自带的wordcount为例,说明四个步骤的工作:
假设输入文件的内容为:
"hello word" 第一行
"hello hadoop" 第二行
一:map
a,mapper用Job的setMapperClass指定
b,map的输入通过Job的setInputFormat指定,默认问TextInputFormat(每行为一条记录,以偏移为key,行内容为value),还可以指定为KeyValueTextInputFormat(每行为一条记录,分隔符之前为key,之后为value,默认的分隔符是\t,通过mapred.textinputformat.seperator指定),SequenceFileInputFormar(键和值由用户指定,序列文件为hadoop专用的二进制压缩格式),还有NLineInputFormat(与Textnputormat相同,但每个分片一定由N行,N默认为1,通过,mapred.line.input.format.linespermap设定)
c,map的输出与输入格式类似,反推即可,例如对应SequenceFileInputFormat,输出类为SequenceFileOutputFormat
处理输入数据,将数据按照用户想要的<key,value>形式collect起来
在经过这个步骤后,现在的数据为一下形式:<hello 1>,<word 1>,<hello 1>,<hadoop 1>
二:combin
combin是在本地进行的一个reduce的过程,其目的是提高hadoop的效率。
直接将数据交给下一个步骤处理,这个例子中存在两个以hello为键的记录,所以在下一个步骤中需要处理两条<hello 1>的记录,如果先做一次combin,则只需处理一次<hello 2>的记录,这样做的一个好处就是,当数据量很大时,减少很多开销。(直接将partition后的结果交给reduce处理,由于tasktracker并不一定分布在本节点,过多的冗余记录会影响IO,与其在reduce时进行处理,不如在本地先进性一些优化以提高效率)
三:partition
得到map给的记录后,他们该分配给哪些reducer来处理呢?hadoop采用的默认的派发方式是根据散列值来派发的,但是实际中,这并不能很高效或者按照我们要求的去执行任务。例如,经过partition处理后,一个节点的reducer分配到了20条记录,另一个却分配道了10W万条,试想,这种情况效率如何。又或者,我们想要处理后得到的文件按照一定的规律进行输出,假设有两个reducer,我们想要最终结果中part-00000中存储的是"h"开头的记录的结果,part-00001中存储其他开头的结果,这些默认的partitioner是做不到的。所以需要我们自己定制partition来根据自己的要求,选择记录的reducer。
自定义partitioner很简单,只要自定义一个类,并且继承Partitioner类,重写其getPartition方法就好了,在使用的时候通过调用Job的setPartitionerClass指定一下即可
note:hadoop的partition过程会根据数据格式,选择相应的比较规则对数据进行排序
四:reduce
reduce就是所谓的归并阶段啦,此时我们的数据是以<key,list(value1,value2...)>的方式存储的。以wordcount的例子来说,如果没有执行上面的2步骤的话,此时的记录应该是:<hadoop list(1)>,<hello list(1,1)>,<word list(1)>,如果执行过2步骤的话,那么结果应该是<hadoop list(1)>,<hello list(2)>,<word list(1)>。
此时,只要根据需求,处理key和values即可
最终结果:
hadoop 1
hello 2
word 1