作用
避免任务在reduceTask上的积压。
1、因为实际过程中MapTask的数量要远多于ReduceTask的数量,所以所有的计算压力最终都会落到ReduceTask上,所以就要考虑降低ReduceTask的计算压力 - 可以考虑将ReduceTask一部分的计算前移,在MapTask这一端先进行一次汇总,最后ReduceTask再进行最后的汇总
2、Combiner的执行逻辑和Reducer是一样的,所以实际过程中将Reducer类也作位Combiner类来使用,也就是不需要再开发combiner类,设置一下即可
3、Combiner的作用是为了减少数据总条数,不改变计算结果
4、如果需要添加合并类,只需要在Driver中添加job.setCombinerClass()即可,设置成reduce.class即可
5、Combiner一定程度上确实可以提高效率,在实际开发中也建议尽量增加Combiner,但是注意并不是所有的场景都适合使用Combiner。例如求和、去重、获取最值等可以使用Combiner,但是求平均值等场景不适合于Combiner
6、分区不会对combiner造成影响。在map阶段发生的合并也会考虑分区。不会把不同分区的值进行合并的。
7、不是所有的Reducedr类都可以作为Combiner的,需要看类型符不符合。
Driver类中执行Combiner
// 指定合并类
job.setCombinerClass(SortProfitReducer.class);
演示
mapper对象
package partiScore;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
import java.io.IOException;
public class SumMapper extends Mapper<LongWritable, Text,Text,Score> {
@Override
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
//1 zhang 89
String[] s = value.toString().split(" ");
Score score = new Score();
score.setMonth(Integer.parseInt(s[0]));
score.setName(s[1]);
score.setScore(Integer.parseInt(s[2]));
context.write(new Text(score.getName()),score);
}
}
reducer对象
package partiScore;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
import java.io.IOException;
public class SumReducer extends Reducer<Text,Score, Text, IntWritable> {
@Override
protected void reduce(Text key, Iterable<Score> values, Context context) throws IOException, InterruptedException {
int sum = 0;
for(Score s:values){
sum+=s.getScore();
}
context.write(key,new IntWritable(sum));
}
}
报错
reducer类接受的输入类型是Text以及Score类型,输出类型是Text和IntWritable,两个类型的不一致导致将Reducer类设置成combiner之后,发生报错。
类型错误,因为combiner的结果应该是reducer的输入,所以combiner的输出类型应该是Text以及Score类型,不可以使用已经存在的reducer来作为合并类,这里不行。
解决
可能会发生combiner的地方
1、发生在maptask自带的环形缓冲区当中
2、发生在溢写文件进行merge的时候,输出成final out文件时,设置了combiner,且溢写文件个数大于等于3时,merge过程中会发生combine合并