Map/Reduce的GroupingComparator排序

一、背景

排序对于MR来说是个核心内容,如何做好排序十分的重要,这几天写了一些,总结一下,以供以后读阅。

二、准备

1、hadoop版本是0.20.2

2、输入的数据格式(这个很重要,看清楚格式),名称是secondary.txt:

   
   
[java] view plain copy
  1. abc     123  
  2. acb     124  
  3. cbd     523  
  4. abc     234  
  5. nbc     563  
  6. fds     235  
  7. khi     234  
  8. cbd     675  
  9. fds     971  
  10. hka     862  
  11. ubd     621  
  12. khi     123  
  13. fds     321  

仔细看下,数据文件第一列是字母,第二列是数字,我要做的就是结合这组数据进行一些排序的测试。

3、代码框架,因为接下来的测试改动都是针对部分代码的修改,框架的代码是不会改变的,所以先把主要代码贴在这里。

代码分为2部分:自定义的key和主框架代码(注意看下红色部分)。先贴上主框架代码:

MyGrouping.java

[java]  view plain copy
  1. import org.apache.hadoop.conf.Configuration;  
  2. import org.apache.hadoop.fs.Path;  
  3. import org.apache.hadoop.io.LongWritable;  
  4. import org.apache.hadoop.io.Text;  
  5. import org.apache.hadoop.io.WritableComparator;  
  6. import org.apache.hadoop.mapreduce.Job;  
  7. import org.apache.hadoop.mapreduce.Mapper;  
  8. import org.apache.hadoop.mapreduce.Partitioner;  
  9. import org.apache.hadoop.mapreduce.Reducer;  
  10. import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;  
  11. import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;  
  12. import org.apache.hadoop.util.GenericOptionsParser;  
  13.   
  14. import com.run.lenged.business.TextPair;  
  15.   
  16. public class MyGrouping {  
  17.   
  18.     /** 
  19.      * Map 
  20.      *  
  21.      * @author Administrator 
  22.      */  
  23.     public static class MyGroupingMap extends Mapper<LongWritable, Text, TextPair, Text> {  
  24.         protected void map(LongWritable key, Text value,  
  25.                 org.apache.hadoop.mapreduce.Mapper<LongWritable, Text, TextPair, Text>.Context context)  
  26.                 throws java.io.IOException, InterruptedException {  
  27.             String arr[] = value.toString().split("/t");  
  28.             if (arr.length != 2) {  
  29.                 return;  
  30.             }  
  31.             TextPair tp = new TextPair();  
  32.             tp.set(new Text(arr[0]), new Text(arr[1]));  
  33.             context.write(tp, new Text(arr[1]));  
  34.         }  
  35.     }  
  36.   
  37.     /** 
  38.      * 按照Hashcode值来进行切分 
  39.      *  
  40.      * @author Administrator 
  41.      */  
  42.     public static class MyGroupingPartition extends Partitioner<TextPair, Text> {  
  43.         @Override  
  44.         public int getPartition(TextPair key, Text value, int numPartitions) {  
  45.             return (key.hashCode() & Integer.MAX_VALUE) % numPartitions;  
  46.         }  
  47.     }  
  48.   
  49.     /** 
  50.      * group进行排序 
  51.      *  
  52.      * @author Administrator 
  53.      */  
  54.     @SuppressWarnings("unchecked")  
  55.     public static class MyGroupingGroup extends WritableComparator {  
  56.         //代码变动部分  
  57.     }  
  58.   
  59.     /** 
  60.      * reduce 
  61.      *  
  62.      * @author Administrator 
  63.      */  
  64.     public static class MyGroupingReduce extends Reducer<TextPair, Text, Text, Text> {  
  65.         protected void reduce(TextPair key, java.lang.Iterable<Text> value,  
  66.                 org.apache.hadoop.mapreduce.Reducer<TextPair, Text, Text, Text>.Context context)  
  67.                 throws java.io.IOException, InterruptedException {  
  68.             StringBuffer sb = new StringBuffer();  
  69.             while (value.iterator().hasNext()) {  
  70.                 sb.append(value.iterator().next().toString() + "_");  
  71.             }  
  72.             context.write(key.getFirst(), new Text(sb.toString().substring(0, sb.toString().length() - 1)));  
  73.         }  
  74.     }  
  75.   
  76.     public static void main(String args[]) throws Exception {  
  77.         Configuration conf = new Configuration();  
  78.         GenericOptionsParser parser = new GenericOptionsParser(conf, args);  
  79.         String[] otherArgs = parser.getRemainingArgs();  
  80.         if (args.length != 2) {  
  81.             System.err.println("Usage: NewlyJoin <inpath> <output>");  
  82.             System.exit(2);  
  83.         }  
  84.   
  85.         Job job = new Job(conf, "MyGrouping");  
  86.         // 设置运行的job  
  87.         job.setJarByClass(MyGrouping.class);  
  88.         // 设置Map相关内容  
  89.         job.setMapperClass(MyGroupingMap.class);  
  90.         job.setMapOutputKeyClass(TextPair.class);  
  91.         job.setMapOutputValueClass(Text.class);  
  92.         job.setPartitionerClass(MyGroupingPartition.class);  
  93.           
  94.         job.setGroupingComparatorClass(MyGroupingGroup.class);  
  95.           
  96.         // 设置reduce  
  97.         job.setReducerClass(MyGroupingReduce.class);  
  98.         job.setOutputKeyClass(Text.class);  
  99.         job.setOutputValueClass(Text.class);  
  100.         // 设置输入和输出的目录  
  101.         FileInputFormat.addInputPath(job, new Path(otherArgs[0]));  
  102.         FileOutputFormat.setOutputPath(job, new Path(otherArgs[1]));  
  103.         // 执行,直到结束就退出  
  104.         System.exit(job.waitForCompletion(true) ? 0 : 1);  
  105.     }  
  106. }  

TextPair.java

[java]  view plain copy
  1. import java.io.DataInput;  
  2. import java.io.DataOutput;  
  3. import java.io.IOException;  
  4.   
  5. import org.apache.hadoop.io.Text;  
  6. import org.apache.hadoop.io.WritableComparable;  
  7.   
  8. public class TextPair implements WritableComparable<TextPair> {  
  9.   
  10.     private Text first;  
  11.     private Text second;  
  12.   
  13.     public TextPair() {  
  14.         set(new Text(), new Text());  
  15.     }  
  16.   
  17.     public void set(Text first, Text second) {  
  18.         this.first = first;  
  19.         this.second = second;  
  20.     }  
  21.   
  22.     public Text getFirst() {  
  23.         return first;  
  24.     }  
  25.   
  26.     public Text getSecond() {  
  27.         return second;  
  28.     }  
  29.   
  30.     @Override  
  31.     public void readFields(DataInput in) throws IOException {  
  32.         first.readFields(in);  
  33.         second.readFields(in);  
  34.     }  
  35.   
  36.     @Override  
  37.     public void write(DataOutput out) throws IOException {  
  38.         first.write(out);  
  39.         second.write(out);  
  40.     }  
  41.   
  42.     @Override  
  43.     public int compareTo(TextPair o) {  
  44.         int cmp = first.compareTo(o.first);  
  45.         if (cmp != 0) {  
  46.             return cmp;  
  47.         } else {  
  48.             return second.compareTo(o.second);  
  49.         }  
  50.     }  
  51. }  



三、测试前提

1、首先提一个需求,我们结合需求来测试,然后再扩散开。
需求内容是:如果第一列值相同,第二列值叠加,并对第二列值进行升序排序。最后输出的时候,按照第一列值的升序排序输出。
2、需求实现。
根据上面的需求,我们可以分析一下:
需要对第一个字段和第二个字段都进行排序,那么单纯的利用MR框架对key迭代输出,value累加是不行的。因为value是没有进行排序。
所以我们需要做一些改动,定义key为符合组建。TextPair.java类就是自定义的key。
一般来说如果要对key和value同时做排序,那么,自定义的组合key的格式第一个值是第一个字段,第二个值就是第二个字段。
3、那么我们就定义一个job.setGroupingComparatorClass(MyGroupingGroup.class);代码如下:

[javascript]  view plain copy
  1. public static class MyGroupingGroup extends WritableComparator {  
  2.         public int compare(WritableComparable a, WritableComparable b) {  
  3.             return mip1.getFirst().compareTo(mip2.getFirst());  
  4.     }  
  5.   
  6.   
  7.         protected MyGroupingGroup() {  
  8.             super(TextPair.classtrue);  
  9.         }  
  10.   
  11.         @Override  
  12.             TextPair mip1 = (TextPair) a;  
  13.             TextPair mip2 = (TextPair) b;  
  14.         }  


只对输出的复合组建第一项值进行排序。输出的结果如下:

[java]  view plain copy
  1. abc 123_234  
  2. cbd 523_675  
  3. khi 123_234  
  4. ubd 621  
  5. nbc 563  
  6. acb 124  
  7. fds 235_321_971  
  8. hka 862  


4、查看结果,我们可以看出,基本满足了上面的需求。那么接下来,我们就将做个测试,来实现一下MR的排序功能。

四、Group按第二个字段值进行排序测试

1、修改一下group的排序方式,针对第二个值进行合并排序,代码如下:

[java]  view plain copy
  1. public static class MyGroupingGroup extends WritableComparator {  
  2.         protected MyGroupingGroup() {  
  3.             super(TextPair.classtrue);  
  4.         }  
  5.   
  6.         @Override  
  7.         public int compare(WritableComparable a, WritableComparable b) {  
  8.             TextPair mip1 = (TextPair) a;  
  9.             TextPair mip2 = (TextPair) b;  
  10.             return mip1.getSecond().compareTo(mip2.getSecond());  
  11.             //return mip1.getFirst().compareTo(mip2.getFirst());  
  12.         }  
  13.     }  

2、reduce的输出稍微改下,将第2个字段也输出,方便查看,代码如下:

[java]  view plain copy
  1. context.write(key.getFirst(), new Text(sb.toString().substring(0, sb.toString().length() - 1)));  

reduce输出的结果:

[html]  view plain copy
  1. abc_123 123  
  2. abc_234 234  
  3. acb_124 124  
  4. cbd_523 523  
  5. cbd_675 675  
  6. fds_235 235  
  7. fds_321 321  
  8. fds_971 971  
  9. hka_862 862  
  10. khi_123 123  
  11. khi_234 234  
  12. nbc_563 563  
  13. ubd_621 621  

3、看到结果,第一反应就是没有按照我的要求,按第二个值进行排序操作。

其实不是,这个结果确实是进行了group的排序,只是说遇到没有符合合并结果数据。所以,看起来没有进行排序。

在这里有个概念,就是group到底是在什么时候做的排序,原文是这样写的:

Job.setGroupingComparatorClass(Class<? extends RawComparator> cls)  
Define the comparator that controls which keys are grouped together 
for a single call to Reducer.reduce(Object, Iterable, org.apache.hadoop.mapreduce.Reducer.Context)

我尝试翻译了一下(英文水平实在是有限,不对的地方还望各位指出):

在一个reduce的调用过程中,定义一个comparator,对分组在一起的key进行排序。

通过上面这句话就可以理解,为什么khi_123 123abc_123 123没有叠加在一起。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值