一、combiner 合并
1.1 工作位置
如下图回顾,combiner是MapReduce阶段的可选流程。
它的工作位置:
1. kv缓冲区中溢写到磁盘时可以使用combiner (只要设置,无条件使用)
2. 每个mapTask的所有数据从缓冲区写到磁盘后,在进行归并的时候可以使用combiner(满足条件使用,溢写次数>=3)
1.2 为什么要使用combiner
我们知道,MapReduce 使用Mapper将数据处理成一个个的<k,value> 键值对,在网络节点间对其进行整理(shuffle),然后使用Reduce处理数据并进行最终输出。
在这个过程中,我们可以看到两个性能瓶颈:
-
如果我们有1亿个数据,Mapper会生成1亿个键值对在网络间传输,但如果我们只是求累加值,那么明显可以在Mapper阶段完成累加值,然后将得到的值传给reduce,这样就变成一个键值对在网络间传输,可以提高程序效率。 总结: 网络带宽严重被占降低程序效率
-
太多的键值对最终聚集于一个单一的Reduce之上,从而降低程序的性能。
1.3 combiner概述
为了解决上面的瓶颈,combiner横空出世。它是MpaReduce 中Mapper和Reduce之外的一种组件。目的就是在每个MapTask中将输出的kv提前进行合并,能降低map到Reduce的传输数量及最终reduce处理的数据量。
【注意】:
- combiner 组件的父类就是Reducer
- combiner 和Reducer的区别在于运行的位置,combiner是在每一个MapTask所在的节点运行,而Reducer是接收全局所有的Mapper的输出结果。
- combiner能够应用的前提是不能影响最终的业务逻辑。
1.4 自定义conbiner步骤
(a)自定义一个Combiner继承Reducer,重写Reduce方法
public class WordcountCombiner extends Reducer<Text, IntWritable, Text,IntWritable>{
@Override
protected void reduce(Text key, Iterable<IntWritable> values,Context context) throws IOException, InterruptedException {
// 1 汇总操作
int count = 0;
for(IntWritable v :values){
count += v.get();
}
// 2 写出
context.write(key, new IntWritable(count));
}
}
(b)在job去驱动类中设置:
job.setCombinerClass(WordcountCombiner.class);
二、combiner 合并案例实操
在之前的wordcount案例,新增一个Combiner类继承Reducer
package com.atguigu.mr.combiner;
import java.io.IOException;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
public class WordcountCombiner extends Reducer<Text, IntWritable, Text, IntWritable>{
IntWritable v = new IntWritable();
@Override
protected void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
// 1 汇总
int sum = 0;
for(IntWritable value :values){
sum += value.get();
}
v.set(sum);
// 2 写出
context.write(key, v);
}
}
使用前
使用combiner后: