MapReduce二次排序

默认情况下,Map输出的结果会对Key进行默认的排序,但个别需求要求对Key排序的同时还需要对Value进行排序
这时候就要用到二次排序了。

本章以hadoop权威指南中计算每年最大气温值为例,原始数据杂乱无章

2008 33
2008 23
2008 43
2008 24
2008 25
2008 33
2008 13
2008 22
2008 33
2008 33
2009 23
2009 43
2009 24
2009 25
2009 33
2007 15
2007 22
2007 30
2007 100

 

1.定义组合key
 将Key和Value组合形成新的key(NewKey),  NewKey要实现WritableComparable接口

我这边定义了一个新类PairTemp.class,含有year(年份)和temp(气温)这两个字段

 必须实现下面3个方法
 compareTo:排序 (年份升序,气温降序)
 write:序列化
 readFields:反序列化
 序列化和反序列化的顺序必须一致 

public class PairTemp implements WritableComparable<PairTemp> {
    private int year;
    private int temp;

    public PairTemp() {
    }

    public PairTemp(int year, int temp) {
        this.year = year;
        this.temp = temp;
    }

    public int getYear() {
        return year;
    }

    public void setYear(int year) {
        this.year = year;
    }

    public int getTemp() {
        return temp;
    }

    public void setTemp(int temp) {
        this.temp = temp;
    }

    public int compareTo(PairTemp o) {
        if (this.year == o.getYear()) {
            //气温降序
            return  -(this.temp-o.getTemp());
        }else {
            return this.year - o.getYear();
        }
    }

    public void write(DataOutput dataOutput) throws IOException {
        dataOutput.writeInt(year);
        dataOutput.writeInt(temp);
    }

    public void readFields(DataInput dataInput) throws IOException {
        year = dataInput.readInt();
        temp = dataInput.readInt();
    }

    @Override
    public String toString() {
        return "PairTemp{" +
                "year=" + year +
                ", temp=" + temp +
                '}';
    }
}

2.map需要自定义分区,继承Partitioner
  重写getPartition方法
  使map生成的数据按照自定义分区方法,进入不同的分区

  例如:年份year%reduce数量

public class myPartitioner extends Partitioner<PairTemp, NullWritable> {
    @Override
    public int getPartition(PairTemp pairTemp, NullWritable nullWritable, int i) {
        return pairTemp.getYear() % i;
    }
}

  这样同一个年份的数据进入了同一个分区,年份升序,气温降序,结果如下

  

2007    100
2007    30
2007    22
2007    15
2008    43
2008    33
2008    33
2008    33
2008    33
2008    25
2008    24
2008    23
2008    22
2008    13
2009    43
2009    33
2009    25
2009    24
2009    23

此时距离我们获取每年都最大值这一要求更近一步来,看上去,我们只需取每一年的第一条记录就可以了

但上面的记录整体是做为一个NewKey,就要用到分组的思想来,我希望按年分成3组,2007,2008,2009各一组

3.自定义分组,继承WritableComparator
   重写了compare方法

public class myGroupComparator extends WritableComparator {

    protected myGroupComparator() {
        super(PairTemp.class,true);
    }

    @Override
    public int compare(WritableComparable a, WritableComparable b) {
        PairTemp p1=(PairTemp)a;
        PairTemp p2=(PairTemp)b;
        
        return p1.getYear()-p2.getYear();
    }
}

  分组后的数据发送给reduce处理

  4. reduce端处理

在这段代码中输出了第一个key值,也就是按照年份升序,气温降序后的每一组的第一条数据

public class maxReducer extends Reducer<PairTemp, NullWritable, IntWritable, IntWritable> {
    @Override
    protected void reduce(PairTemp key, Iterable<NullWritable> values, Context context) throws IOException, InterruptedException {

        context.write(new IntWritable(key.getYear()), new IntWritable(key.getTemp()));
        System.out.println("--------reduce---------------");
        System.out.println("第一条key信息:"+key);

        for(NullWritable temp:values){
            System.out.println(key.getYear()+":"+key.getTemp());
        }
        System.out.println("最后一条key信息:"+key);

    }
}

看打印结果就能理解了

--------reduce---------------
第一条key信息:PairTemp{year=2007, temp=100}
2007:100
2007:30
2007:22
2007:15
最后一条key信息:PairTemp{year=2007, temp=15}
--------reduce---------------
第一条key信息:PairTemp{year=2008, temp=43}
2008:43
2008:33
2008:33
2008:33
2008:33
2008:25
2008:24
2008:23
2008:22
2008:13
最后一条key信息:PairTemp{year=2008, temp=13}
--------reduce---------------
第一条key信息:PairTemp{year=2009, temp=43}
2009:43
2009:33
2009:25
2009:24
2009:23
最后一条key信息:PairTemp{year=2009, temp=23}

 

4.驱动类额外增加

 setPartitionerClass和setGroupingComparatorClass

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值