MR全排序和二次排序

一、全排序

我们知道,mapreduce是可以做到分区排序的,即区内有序,但分区间不保证有序。

我们要想实现全排序,有两种思路:

  1. 把数据都发送到一个reducer中进行排序
  2. 把一个范围内的数据发送到一个reducer中排序,然后把多个reducer排序的结果合并在一起。如我们对[0,100]的数据,那么就可以先分成[0, 25],(25, 50], (50, 75], (75, 100] 四个reducer,然后对其中的数据排序

上述两种思路都各有问题:

第一种思路把所有的数据发送到一个reducer中,会导致那个reducer的压力过大;

第二种思路如果采取均分的思路,同样会产生数据不均匀的情况,如(25, 50]的数据可能最多,占了总量的80%,其余3个分区只占20%,依然会对一个reducer造成太大压力

为了解决第二种思路的问题,我们可以提前探查数据分布,然后手动做分区调整;同时MR也为我们提供了解决方案,可以通过使用RandomSampler和TotalOrderPartitioner来解决。

大概流程就是RandomSampler先通过随机采样得到数据分布,然后它会生成分区文件,然后TotalOrderPartitioner读取这个分区文件来确定分区

在这里插入图片描述

二、二次排序

冷知识:map溢写做排序时,如果元素个数小于13,则用冒泡排序,否则用快排

mr在进行reduce之前,会默认对key进行排序,但是对于每个key,它的value并没有排序,mr在大多数情况下也并不需要value有序,但是在一些特殊情况下,如果value有序,将会极大地提升性能。

如我们对某个key求count(distinct value),这个时候,如果key中的value也有序,我们就可以直接遍历key中的value,比较其与上一个是否相等即可,而不需要将其保存在Set中;当然我们也可以在reduce端使用堆排序来实现value有序,但是这种会有内存爆掉的风险。

这里还是用书中的例子来进行说明二次排序的实现,如我们要求每一年的最高气温,其中year是key,temperatures是value,如果temperatures可以按照降序排列的话的话,我们只需要拿每个year的第一个值就可以了,而不用遍历其他的value。

为了实现这个功能,需要如下几步:

  1. 生成组合key:将year和temperature同时认为是key

  2. 定义排序器对组合key进行排序,即先对year排序,再对temperature排序

    mr在底层将数据写入到缓冲区后,数据都是序列化以后的,所以是直接在byte数组上比大小;

    b:要比较的byte数组;s:开始位置;l:长度

    /** A Comparator that compares serialized IntPair. */
    public static class Comparator extends WritableComparator {
      public Comparator() {
        super(IntPair.class);
      }
    
      public int compare(byte[] b1, int s1, int l1,
                         byte[] b2, int s2, int l2) {
        return compareBytes(b1, s1, l1, b2, s2, l2);
      }
    }
    

在这里插入图片描述

  1. 定义分区器,使用组合key中的year进行分区,保证同一个year的数据可以到达同一个分区;

    /**
    * Partition based on the first part of the pair.
    */
    public static class FirstPartitioner extends Partitioner<IntPair,IntWritable>{
      @Override
      public int getPartition(IntPair key, IntWritable value,
                              int numPartitions) {
        return Math.abs(key.getFirst() * 127) % numPartitions;
      }
    }
    

在这里插入图片描述

  1. 定义Reduce端的group比较器,只用year进行比较,来判断是否是属于同一个reduce,具体代码体现在reduce找nextKey()的时候;如果这里不设置比较器,reduce会默认使用第2步定义的比较器,即只有year和temperature都相等的时候才会把它当作同一个key处理,所以需要重新定义比较器

    /**
     * Compare only the first part of the pair, so that reduce is called once
     * for each value of the first part.
    */
    public static class FirstGroupingComparator
      implements RawComparator<IntPair> {
      @Override
      public int compare(byte[] b1, int s1, int l1, byte[] b2, int s2, int l2) {
        //这里和第2步的不同点在于,这里并没有直接使用l1和l2,而是使用的是Integer的长度,这种就可以保证只比较year
        return WritableComparator.compareBytes(b1, s1, Integer.SIZE/8,
                                               b2, s2, Integer.SIZE/8);
      }
    }
    
    

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值