Hadoop3教程(十五):MapReduce中的Combiner

文章目录

(103)Combiner概述

什么是Combiner

Combiner(即合并)是MR里shuffle的一项可选流程,位于Map阶段和Reduce阶段之间,是MR中,除Mapper和Reducer之外的一种组件,但并不是默认存在的组件,其可有也可无。

Combiner有什么用处

主要是用来减少数据量。

比如说在WordCount的案例里,我们可以针对像(a,1)、(a,1)、(a,1)这种完全一样的数据,启用Combiner进行一个简单的聚合,即转换成(a,3)这样的数据。这样做的好处很明显,就是大大减少了输入到Reduce的数据量,以上面例子为例,3条数据直接变成了1条,从而减少了reduce处理的资源压力。

之前大概提过,它的使用场景有两个地方:

第一个场景,是mapper每次溢写到磁盘的时候,每当溢写的时候就可以进行Combiner操作。每个分区内部就开始简单合并。

第二个场景,是在单个MapTask的所有(或部分)的map()都溢写完成后,会有一个归并操作,将所有溢写的文件进行分区归并,待合并完成后,同样可以对每个分区进行一个Combiner操作,减少数据量。

Combiner有什么特点

汇总下Combiner一些特点

  • Combiner的父类是Reducer,即它继承的就是Reducer类;
  • Combiner和Reducer的区别就在于运行的位置,Combiner是在每一个MapTask所在的节点上运行,Reducer是接收所有Mapper的输出;
  • Combiner的意义就是对每一个MapTask(或者说是对自己所在的MapTask)的输出进行局部汇总,以减少网络传输量。
  • 并不是所有的场景都可以使用Combiner,具体区别可以再摸索下,主要是不能影响最终的业务逻辑。

因此,我们可以这么说: Combiner就是运行在一个MapTask上的Reducer,即局部汇总,而真正的Reducer是可以面向所有MapTask的。

另外,如何理解"不能影响最终的业务逻辑"这句话呢?

比如说,当前的业务逻辑是算输入数据的平均值,那我提前对每个MapTask做Combiner,来计算每个MapTask的平均值,然后把结果传给Reducer来计算全部MapTask的平均值,这样子可以吗?

这当然是不行的。

假设两个MapTask,一个接收数字3、5,7,另一个接收数字2和6,分别计算平均值,那就是5和4,再传进Reducer计算平均值:(5+4)/2=4.5。

但是其实(3+5+7+2+6)/5 = 4.6,上面结果算的明显不对。

所以, 是否可以使用Combiner,以及使用什么样的Combiner,都得以不影响最终业务逻辑为前提,不能随便应用。

如何自定义Combiner

如何自定义Combiner?

继承Reducer类,重写Reduce()就可以。

java 复制代码
public class WordCountCombiner extends Reducer<Text, IntWritable, Text, IntWritable> {

    private IntWritable outV = new IntWritable();

    @Override
    protected void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {

        int sum = 0;
        for (IntWritable value : values) {
            sum += value.get();
        }
     
        outV.set(sum);
     
        context.write(key,outV);
    }
}

最后在驱动器里注册:

java 复制代码
job.setCombinerClass(WordCountCombiner.class);

(104)Combiner合并案例实操

如何从日志里查看Combiner

如何从打印的日志里来查看Combiner的详情呢?

打印的日志里,有一个Map-Reduce Framework区域,如下图,红色框出来的部分就是combine的运行结果。

combine input records代表输入到Combiner的数据行数,combine output records则表示经过Combiner处理后,输出的数据行数。

下图左边表示未启用自定义Combiner时的输出结果,右边表示启用自定义Combiner后的输出结果。

可以看到,启用前Reduce shuffle bytes是156字节,启用后是66字节,说明数据在传到reduce前经过了自定义Combiner的处理,数据量减小了。

如果不存在Reduce阶段,会发生什么

如果没有Reduce阶段,就不需要设置Combiner了。

如果我们在驱动类里设置job.setNumReduceTasks(0),即取消Reduce阶段,那么Combiner还会生效么?

答案是不会,Hadoop会将mapper的处理结果作为输出,持久化到文件。以WordCount案例为例,我们最终在文件里看到的就是:

<a,1>
<b,1>
<a,1>
....

为什么会这样呢?

是因为Combiner是存在于Mapper和Reducer之间的shuffle阶段,如果没有reduce的话,那么Reducer阶段不存在,所以整个shuffle阶段也就不存在了,Hadoop会直接将Mapper的处理结果导出。

自定义Combiner的两种方式

还是以WordCount来举例吧。

第一种方式,增加一个WordCountCombiner来继承Reducer,这个上一小节其实讲过了:

java 复制代码
package com.atguigu.mapreduce.combiner;

import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
import java.io.IOException;

public class WordCountCombiner extends Reducer<Text, IntWritable, Text, IntWritable> {

private IntWritable outV = new IntWritable();

    @Override
    protected void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {

        int sum = 0;
        for (IntWritable value : values) {
            sum += value.get();
        }

        //封装outKV
        outV.set(sum);

        //写出outKV
        context.write(key,outV);
    }
}

然后在驱动类里声明:

java 复制代码
// 指定需要使用combiner,以及用哪个类作为combiner的逻辑
job.setCombinerClass(WordCountCombiner.class);

但是!

实际上如果你看过教程里的代码,会发现,我们自定义的这个Combiner,里面的reduce()跟我们自定义的WordCountReducer类的reduce()一模一样,甚至整个类都是一样的,因为所谓的Combiner就是运行在单个MapTask的Reducer,再加上我们的业务要求,处理逻辑自然完全一致。

所以在这种情况下,我们根本不需要再定义一个Combiner类,直接使用自定义Reducer类来声明就可以。

这就是第二种方案,即我们只需要在驱动类里直接这么写:

java 复制代码
// 指定需要使用Combiner,以及用哪个类作为Combiner的逻辑
job.setCombinerClass(WordCountReducer.class);

齐活,简直完美。

参考文献

  1. 【尚硅谷大数据Hadoop教程,hadoop3.x搭建到集群调优,百万播放】
相关推荐
瓜牛_gn28 分钟前
mysql特性
数据库·mysql
时差9531 小时前
MapReduce 的 Shuffle 过程
大数据·mapreduce
奶糖趣多多1 小时前
Redis知识点
数据库·redis·缓存
kakwooi2 小时前
Hadoop---MapReduce(3)
大数据·hadoop·mapreduce
CoderIsArt3 小时前
Redis的三种模式:主从模式,哨兵与集群模式
数据库·redis·缓存
windy1a3 小时前
【c知道】Hadoop工作原理。
hadoop
师太,答应老衲吧5 小时前
SQL实战训练之,力扣:2020. 无流量的帐户数(递归)
数据库·sql·leetcode
Channing Lewis6 小时前
salesforce case可以新建一个roll up 字段,统计出这个case下的email数量吗
数据库·salesforce
毕业设计制作和分享7 小时前
ssm《数据库系统原理》课程平台的设计与实现+vue
前端·数据库·vue.js·oracle·mybatis
ketil277 小时前
Redis - String 字符串
数据库·redis·缓存