hadoop的分区、分组

***相同组内的k-v,由同一次的reduce方法处理

一、为什么写

分区和分组在排序中的作用是不一样的,今天早上看书,又有点心得体会,记录一下。

二、什么是分区

1、还是举书上的例子,在8.2.4章节的二次排序过程中,用气温举例,所以这里我也将这个例子说一下。

源数据内容

1900 35°C
1900 34°C
1900 34°C
...
1901 36°C
1901 35°C

书上的例子是为了去除一年当中气温最高的值,那么将年份和气温做了一个复合的key.

2、通过设置了partitioner来进行分区(这里注意了,分区是通过partitioner来进行的)。因为分区是按照年份来进行,所以同年的数据就可以

分区到一个reducer中。但是这样的分区是不能做到对气温划分的,所以分区后的结果如下:

7SG([3$_~JFIOM$6PIRVT6E

上面这个图是书上的,我只是截取下来而已。可以看到,partition实现了年份同一分区,但是不是一个分组。注意看后面的2条竖线,通过截断表示。

3、如果想把同一年份的气温分组到一起,那么需要做分组的控制。在reducer中以年份部分来分组值,那么同一年的记录就会分到同一个reduce组中。

结果如下:

image

注意看一下后面2条竖线,和上面的对比,分区和分组相同,那么在reduce输出的时候,只需要取第一个value就能达到输出一年最高气温的目的。


3.1 默认的分组

  在Hadoop中的默认分组规则中,也是基于Key进行的,会将相同key的value放到一个集合中去。这里以上面的例子继续看看分组,因为我们自定义了一个新的key,它是以两列数据作为key的,因此这6行数据中每个key都不相同,也就是说会产生6组,它们是:1 1,2 1,2 2,3 1,3 2,3 3。而实际上只可以分为3组,分别是1,2,3。

  现在首先改写一下reduce函数代码,目的是求出第一列相同时第二列的最小值,看看它会有怎么样的分组:

复制代码
    public static class MyReducer extends
            Reducer<MyNewKey, LongWritable, LongWritable, LongWritable> {
        protected void reduce(
                MyNewKey key,
                java.lang.Iterable<LongWritable> values,
                Reducer<MyNewKey, LongWritable, LongWritable, LongWritable>.Context context)
                throws java.io.IOException, InterruptedException {
            long min = Long.MAX_VALUE;
            for (LongWritable number : values) {
                long temp = number.get();
                if (temp < min) {
                    min = temp;
                }
            }

            context.write(new LongWritable(key.firstNum), new LongWritable(min));
        };
    }
复制代码

  其运行结果为:

1    1
2    1
2    2
3    1
3    2
3    3

  但是我们预期的结果为:

复制代码
#当第一列相同时,求出第二列的最小值
3    3
3    2
3    1
2    2
2    1
1    1
-------------------
#预期结果应该是
3    1
2    1
1    1
复制代码

3.2 自定义分组

  为了针对新的key类型作分组,我们也需要自定义一下分组规则:

  (1)编写一个新的分组比较类型用于我们的分组:

复制代码
    private static class MyGroupingComparator implements
            RawComparator<MyNewKey> {

        /*
         * 基本分组规则:按第一列firstNum进行分组
         */
        @Override
        public int compare(MyNewKey key1, MyNewKey key2) {
            return (int) (key1.firstNum - key2.firstNum);
        }

        /*
         * @param b1 表示第一个参与比较的字节数组
         * 
         * @param s1 表示第一个参与比较的字节数组的起始位置
         * 
         * @param l1 表示第一个参与比较的字节数组的偏移量
         * 
         * @param b2 表示第二个参与比较的字节数组
         * 
         * @param s2 表示第二个参与比较的字节数组的起始位置
         * 
         * @param l2 表示第二个参与比较的字节数组的偏移量
         */
        @Override
        public int compare(byte[] b1, int s1, int l1, byte[] b2, int s2, int l2) {
            return WritableComparator.compareBytes(b1, s1, 8, b2, s2, 8);
        }

    }
复制代码

  从代码中我们可以知道,我们自定义了一个分组比较器MyGroupingComparator,该类实现了RawComparator接口,而RawComparator接口又实现了Comparator接口,下面看看这两个接口的定义:

  首先是RawComparator接口的定义:

public interface RawComparator<T> extends Comparator<T> {
  public int compare(byte[] b1, int s1, int l1, byte[] b2, int s2, int l2);
}

  其次是Comparator接口的定义:

public interface Comparator<T> {
    int compare(T o1, T o2);
    boolean equals(Object obj);
}

  在MyGroupingComparator中分别对这两个接口中的定义进行了实现,RawComparator中的compare()方法是基于字节的比较,Comparator中的compare()方法是基于对象的比较。

  在基于字节的比较方法中,有六个参数,一下子眼花了:

Params:

* @param arg0 表示第一个参与比较的字节数组
* @param arg1 表示第一个参与比较的字节数组的起始位置
* @param arg2 表示第一个参与比较的字节数组的偏移量

* @param arg3 表示第二个参与比较的字节数组
* @param arg4 表示第二个参与比较的字节数组的起始位置
* @param arg5 表示第二个参与比较的字节数组的偏移量

  由于在MyNewKey中有两个long类型,每个long类型又占8个字节。这里因为比较的是第一列数字,所以读取的偏移量为8字节。

  (2)添加对分组规则的设置:

  // 设置自定义分组规则
   job.setGroupingComparatorClass(MyGroupingComparator.class);

  (3)现在看看运行结果:


三、总结

1、以上内容是对hadoop全文指南的二次排序的个人理解,可能写的比较晦涩,建议看看8.4.2这个章节。

2、分区和分组是不同的概念,并且进行的阶段也是不同的。

3、一般来说,想要做到分区和分组的排序,key一般都是复合的组合(例如年份和气温构成了key)。

4、分在同一组的<key,value>一定同属一个分区。在一个分区的<key,value>可重载"job.setGroupingComparatorClass(a.class);"中的a类的

compare方法重新定义分组规则,同一组的value做为reduce的输入。

4、写的不对或有错误的地方,可留言或发邮件交流dajuezhao@gmail.com


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值