众所周知,Hadoop框架使用Mapper将数据处理成一个个的key/value键值对,在网络节点间对其进行整理(shuffle),然后使用Reducer处理数据并进行最终输出。这其中假如我们有10亿个数据,Mapper会生成10亿个键值对在网络间进行传输(网络带宽严重被占降低程序效率),所有数据都经过reduce处理,造成Reducer的巨大压力,从而大大降低程序的性能。
为了解决上述问题Combiner横空出世。
Combiner
每一个map都可能会产生大量的本地输出,Combiner的作用就是对map端的输出先做一次合并,以减少在map和reduce节点之间的数据传输量,以提高网络IO性能,是MapReduce的一种优化手段之一,其具体的作用如下所述。
(1)Combiner最基本是实现本地key的聚合,对map输出的key排序,value进行迭代。如下所示:
map : (K1, V1) → list(K2, V2)
combine : (K2, list(V2)) → list(K2, V2)
reduce : (K2, list(V2)) → list(K3, V3)(2)Combiner还有本地reduce功能(其本质上就是一个reduce),例如Hadoop自带的wordcount的例子和找出value的最大值的程序,combiner和reduce完全一致,如下所示:
map : (K1, V1) → list(K2, V2)
combine : (K2, list(V2)) → list(K3, V3)
reduce : (K3, list(V3)) → list(K4, V4)注意:
①、与mapper和reducer不同的是,combiner没有默认的实现,需要显式的设置在conf中才有作用。②、并不是所有的job都适用combiner,只有操作满足结合律的才可设置combiner。combine操作类似于:opt(opt(1, 2, 3), opt(4, 5, 6))。如果opt为求和、求最大值的话,可以使用,但是如果是求中值的话,不适用。
③、Combiner的输出是Reducer的输入,如果Combiner是可插拔的(非必须),添加Combiner绝不能改变最终的计算结果。所以Combiner只应该用于那种Reduce的输入key/value与输出key/value类型完全一致,且不影响最终结果的场景。比如累加,最大值等。
以WordCount为例
// WordCount 的mapper、reducer与之前一样
// WCCombiner (须extends Reducer<KEYIN, VALUEIN, KEYOUT, VALUEOUT>)
// combiner最基本是实现本地key的归并,combiner具有类似本地的reduce功能,
public class WordCReducer
extends Reducer<Text, LongWritable, Text, LongWritable> {
protected void reduce(Text key, java.lang.Iterable<LongWritable> values,
Reducer<Text, LongWritable, Text, LongWritable>.Context context)
throws java.io.IOException, InterruptedException {
long count = 0L;
for (LongWritable value : values) {
count += value.get();
}
context.write(key, new LongWritable(count));
}
}
// 设置Map规约Combiner
job.setCombinerClass(MyCombiner.class);
调试运行的控制台输出信息
Mapper
Mapper输出<hello, 1>
Mapper输出<tom, 1>
Mapper输出<hello, 1>
Mapper输出<kevin, 1>
Mapper输出<tom, 1>Combiner
Combiner输入键值对<hello, 1>
Combiner输入键值对<hello, 1>
Combiner输出键值对<hello, 2>
Combiner输入键值对<kevin, 1>
Combiner输出键值对<kevin, 1>
Combiner输入键值对<tom, 1>
Combiner输入键值对<tom, 1>
Combiner输出键值对<hello, 2>Reducer
Reducer输入键值对<hello, 2>
Reducer输入键值对<kevin, 1>
Reducer输入键值对<tom, 2>