zoukankan      html  css  js  c++  java
  • 使用复合键值对二次排序

    一般来说,利用复合键值对使用MR对键值对进行二次排序(MR默认只是对key排序,自定义的方式还对value排序就称为二次排序),需要做四件事:

    1 定制复合键值对类型
    2 定制分区类(对应job的setPartitionerClass方法)
    3 定制排序比较器类(对应job的setSortComparatorClass方法)
    4 定制分组比较类(对应job的setGroupingComparatorClass方法)

    1)以下代码只是实现了第一点和第二点,也可以实现二次排序。

    A.主类:keySort

     1 package keySort;
     2 import java.io.IOException;
     3 import java.io.File;
     4 
     5 import org.apache.hadoop.conf.Configuration;
     6 import org.apache.hadoop.io.Text;
     7 import org.apache.hadoop.mapreduce.Job;
     8 import org.apache.hadoop.mapreduce.Mapper;
     9 import org.apache.hadoop.mapreduce.Partitioner;
    10 import org.apache.hadoop.mapreduce.Reducer;
    11 import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
    12 import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
    13 import org.apache.hadoop.mapreduce.lib.input.KeyValueTextInputFormat;
    14 import org.apache.hadoop.fs.Path;
    15 public class keySort {
    16     public static class MyMapper extends Mapper<Text, Text, compositeKey, Text> {
    17         private compositeKey comKey=new compositeKey();        
    18         public void map(Text key,Text value,Context context) throws IOException, InterruptedException{
    19             //System.out.println("key:"+key+"value:"+value);
    20             //compositeKey.set(key.toString()+"	"+value.toString());
    21             //System.out.println("compositekey:"+compositeKey+"value:"+value);
    22             comKey.set(key, value);
    23             context.write(comKey,value);
    24         }
    25     }
    26     public static class MyPartitioner extends Partitioner<compositeKey, Text>{
    27         //public Text realKey=new Text();
    28         @Override
    29         public int getPartition(compositeKey key, Text value, int numredtasks) {
    30             // TODO 自动生成的方法存根
    31             //String[] kk=key.toString().split("	");
    32             //realKey.set(kk[0]);
    33             //System.out.println("partition key is:"+realKey.toString());
    34             return ((key.getNaturalKey()&Integer.MAX_VALUE)%numredtasks);
    35         }
    36         
    37     }
    38     public static class MyReducer extends Reducer<compositeKey, Text,
    39     Integer, Text>{
    40         @Override
    41         public void reduce(compositeKey key,Iterable<Text> values,Context context) throws IOException, InterruptedException{
    42             for (Text value : values) {
    43                 context.write(key.getNaturalKey(), value);
    44             }
    45         }
    46     }
    47     public static void main(String[] args) throws Exception
    48     {
    49         if(args.length!=2){
    50             System.err.println("Usage: MatrixMultiply <inputPathM> <inputPathN> <outputPath>");
    51              System.exit(2);
    52                    }
    53         File outfile=new File(args[1]);
    54         if(outfile.isDirectory()&&outfile.exists()){
    55             System.out.println("out exist?"+outfile.exists());
    56             File[] files=outfile.listFiles();
    57             for (File file : files) {
    58                 file.delete();
    59             }
    60             outfile.delete();
    61             System.out.println("out exit?"+outfile.exists());
    62         }
    63         else {
    64             System.out.println("out do not exit");
    65         }
    66         Configuration conf=new Configuration();
    67         Job job=new Job(conf,"SeconderySort");
    68         job.setNumReduceTasks(1);//设置为0的话,并不会执行分区过程,也就不能利用符合
    69         job.setJarByClass(keySort.class);
    70         job.setInputFormatClass(KeyValueTextInputFormat.class);        
    71         job.setMapperClass(MyMapper.class);
    72         job.setMapOutputKeyClass(compositeKey.class);
    73         job.setMapOutputValueClass(Text.class);
    74         job.setPartitionerClass(MyPartitioner.class);
    75         job.setReducerClass(MyReducer.class);
    76         job.setOutputKeyClass(Text.class);
    77         job.setOutputValueClass(Text.class);
    78         FileInputFormat.setInputPaths(job, new Path(args[0]));
    79         FileOutputFormat.setOutputPath(job, new Path(args[1]));
    80         System.exit(job.waitForCompletion(true)?0:1);
    81     }
    82     }

    B.自定义复合键值对类----compositeKey

     1 package keySort;
     2 import java.io.DataInput;
     3 import java.io.DataOutput;
     4 import java.io.IOException;
     5 
     6 import org.apache.hadoop.io.Text;
     7 import org.apache.hadoop.io.WritableComparable;
     8 import org.aspectj.weaver.patterns.ArgsAnnotationPointcut;
     9 //接口Writable并没有使用泛型,但是接口Comparable<T>是泛型,其唯一的方法为:
    10 //int compareTo(T o);所以compositeKey实现接口的时候使用了<compositeKey>
    11 public class compositeKey implements WritableComparable<compositeKey>{
    12     private int naturalKey;
    13     private int secondKey;
    14     public void set(Text naturalkey,Text secondkey ) {
    15         // TODO 自动生成的构造函数存根
    16         this.naturalKey=Integer.parseInt(naturalkey.toString());
    17         this.secondKey=Integer.parseInt(secondkey.toString());        
    18     }
    19     public int getNaturalKey() {
    20         return naturalKey;
    21     }
    22     @Override
    23     //反序列化
    24     public void readFields(DataInput arg0) throws IOException {
    25         // TODO 自动生成的方法存根
    26         this.naturalKey=arg0.readInt();
    27         this.secondKey=arg0.readInt();
    28         
    29     }
    30     @Override
    31     //序列化
    32     public void write(DataOutput arg0) throws IOException {
    33         // TODO 自动生成的方法存根
    34         arg0.writeInt(naturalKey);
    35         arg0.writeInt(secondKey);
    36     }
    37     @Override
    38     public int compareTo(compositeKey o) {
    39         // TODO 自动生成的方法存根
    40         int flag=this.naturalKey>o.naturalKey?1:(this.naturalKey==o.naturalKey?0:-1);
    41         if(flag!=0)
    42             return flag;
    43         return this.secondKey>o.secondKey?1:(this.secondKey==o.secondKey?0:-1);
    44     }
    45 }

    @Override就是覆盖的意思,表示在子类中重写父类中定义的方法,加上这个标示符可以使编译器在编译的时候帮我们检查是否与父类中的函数签名一致(函数名,参数个数,参数类型均相同)。

    第11行可知,实现使用了泛型的接口的类,需要指定数据类型,并且在类中要实现接口中定义的所有方法(接口中的方法都是未实现的抽象方法),WritableComparable接口的定义如下:

    public interface WritableComparable<T> extends Writable, Comparable<T> {
    }

    所以,compositeKey实现此接口的时候形式如下,就是将T换为compositeKey:

     public class compositeKey implements WritableComparable<compositeKey>{

    C.所以,接口WritableComparable接口就只是继承了Writable接口和Comparable接口而已。

    Writable用于对象的序列化和反序列化,所有需要序列化的类都需要继承这个接口,定义如下:

     1 public interface Writable {
     2   /** 
     3    * Serialize the fields of this object to <code>out</code>.
     4    * 
     5    * @param out <code>DataOuput</code> to serialize this object into.
     6    * @throws IOException
     7    */
     8   void write(DataOutput out) throws IOException;
     9 
    10   /** 
    11    * Deserialize the fields of this object from <code>in</code>.  
    12    * 
    13    * <p>For efficiency, implementations should attempt to re-use storage in the 
    14    * existing object where possible.</p>
    15    * 
    16    * @param in <code>DataInput</code> to deseriablize this object from.
    17    * @throws IOException
    18    */
    19   void readFields(DataInput in) throws IOException;
    20 }

    可见compositeKey需要实现write方法(序列化)和readFields(反序列化)方法,其具体实现是:

     1 @Override
     2     //反序列化
     3     public void readFields(DataInput arg0) throws IOException {
     4         // TODO 自动生成的方法存根
     5         this.naturalKey=arg0.readInt();
     6         this.secondKey=arg0.readInt();
     7         
     8     }
     9     @Override
    10     //序列化
    11     public void write(DataOutput arg0) throws IOException {
    12         // TODO 自动生成的方法存根
    13         arg0.writeInt(naturalKey);
    14         arg0.writeInt(secondKey);

    argo.readInt()表示序列化的是int型变量,字符串变量是.readUTF()。

    而Comparable接口中只有一个方法:

    int compareTo(T o);

    所以,compositeKey还需要实现一个方法,就是compareTo方法:

    1 @Override
    2     public int compareTo(compositeKey o) {
    3         // TODO 自动生成的方法存根
    4         int flag=this.naturalKey>o.naturalKey?1:(this.naturalKey==o.naturalKey?0:-1);
    5         if(flag!=0)
    6             return flag;
    7         return this.secondKey>o.secondKey?1:(this.secondKey==o.secondKey?0:-1);
    8     }
    public class compositeKey implements WritableComparable<compositeKey>{

    compositeKey实现接口的时候,使用的泛型就是对应着这里的compareTo方法。关于这个方法的返回值:首先调用形式是a.compareTo(o)

    1,表示a大于o

    0,表示两者相等

    -1,表示a小于o

    E:到这里,程序就可以顺利完成二次排序了,在map端排序的时候,会自动调用复合键值对定义的这个compareTo方法对复合键(compositeKey)排序。

    结果如下:

    排序前:

    排序后:

    2)以上是单机版,稍加修改为集群上跑得程序,并自动删除已存在的output目录:

    keySort:

      1 package keySort;
      2 import java.io.IOException;
      3 //import java.io.File;
      4 
      5 import org.apache.hadoop.conf.Configuration;
      6 import org.apache.hadoop.io.Text;
      7 import org.apache.hadoop.mapreduce.Job;
      8 import org.apache.hadoop.mapreduce.Mapper;
      9 import org.apache.hadoop.mapreduce.Partitioner;
     10 import org.apache.hadoop.mapreduce.Reducer;
     11 import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
     12 import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
     13 import org.apache.hadoop.mapreduce.lib.input.KeyValueTextInputFormat;
     14 import org.apache.hadoop.fs.FileSystem;
     15 import org.apache.hadoop.fs.Path;
     16 public class keySort {
     17     public static class MyMapper extends Mapper<Text, Text, compositeKey, Text> {
     18         private compositeKey comKey=new compositeKey();        
     19         public void map(Text key,Text value,Context context) throws IOException, InterruptedException{
     20             //System.out.println("key:"+key+"value:"+value);
     21             //compositeKey.set(key.toString()+"	"+value.toString());
     22             //System.out.println("compositekey:"+compositeKey+"value:"+value);
     23             comKey.set(key, value);
     24             context.write(comKey,value);
     25         }
     26     }
     27     public static class MyPartitioner extends Partitioner<compositeKey, Text>{
     28         //public Text realKey=new Text();
     29         @Override
     30         public int getPartition(compositeKey key, Text value, int numredtasks) {
     31             // TODO 自动生成的方法存根
     32             //String[] kk=key.toString().split("	");
     33             //realKey.set(kk[0]);
     34             //System.out.println("partition key is:"+realKey.toString());
     35             return ((key.getNaturalKey()&Integer.MAX_VALUE)%numredtasks);
     36         }
     37         
     38     }
     39     public static class MyReducer extends Reducer<compositeKey, Text,
     40     Integer, Text>{
     41         private Text separator=new Text();
     42         @Override
     43         protected void setup(Context context) throws IOException ,InterruptedException {
     44             separator.set("-------------------------");
     45             context.write(null,separator);
     46             
     47         };
     48         
     49         @Override
     50         public void reduce(compositeKey key,Iterable<Text> values,Context context) throws IOException, InterruptedException{
     51             for (Text value : values) {
     52                 context.write(key.getNaturalKey(), value);
     53             }
     54         }
     55     }
     56     public static void main(String[] args) throws Exception
     57     {
     58         if(args.length!=2){
     59             System.err.println("Usage: MatrixMultiply <inputPathM> <inputPathN> <outputPath>");
     60              System.exit(2);
     61                    }
     62         /*本地运行时检测并删除输出文件夹
     63         File outfile=new File(args[1]);
     64         if(outfile.isDirectory()&&outfile.exists()){
     65             System.out.println("out exist?"+outfile.exists());
     66             File[] files=outfile.listFiles();
     67             for (File file : files) {
     68                 file.delete();
     69             }
     70             outfile.delete();
     71             System.out.println("out exit?"+outfile.exists());
     72         }
     73         else {
     74             System.out.println("out do not exit");
     75         }
     76         */
     77         Configuration conf=new Configuration();
     78         FileSystem hdfs=FileSystem.get(conf);
     79         Path path=new Path(args[1]);
     80         if(hdfs.exists(path)){
     81             hdfs.delete(path, true);//如果path是一个目录的话,true表示递归删除这个目录,如果path是文件的话,true
     82             //或者false无所谓。另外FileSystem.delete(path)方法已废除。        
     83             System.out.println("output deleted!");
     84         }
     85         else {
     86             System.out.println("no output");
     87         }
     88         Job job=new Job(conf,"SeconderySort");
     89         //job.setNumReduceTasks(1);//设置为0的话,并不会执行分区过程,也就不能利用符合
     90         job.setJarByClass(keySort.class);
     91         job.setInputFormatClass(KeyValueTextInputFormat.class);        
     92         job.setMapperClass(MyMapper.class);
     93         job.setMapOutputKeyClass(compositeKey.class);
     94         job.setMapOutputValueClass(Text.class);
     95         job.setPartitionerClass(MyPartitioner.class);
     96         job.setReducerClass(MyReducer.class);
     97         job.setOutputKeyClass(Text.class);
     98         job.setOutputValueClass(Text.class);
     99         FileInputFormat.setInputPaths(job, new Path(args[0]));
    100         FileOutputFormat.setOutputPath(job, new Path(args[1]));
    101         System.exit(job.waitForCompletion(true)?0:1);
    102     }
    103     }

    compositeKey:

     1 package keySort;
     2 import java.io.DataInput;
     3 import java.io.DataOutput;
     4 import java.io.IOException;
     5 
     6 import org.apache.hadoop.io.Text;
     7 import org.apache.hadoop.io.WritableComparable;
     8 //接口Writable并没有使用泛型,但是接口Comparable<T>是泛型,其唯一的方法为:
     9 //int compareTo(T o);所以compositeKey实现接口的时候使用了<compositeKey>
    10 public class compositeKey implements WritableComparable<compositeKey>{
    11     private int naturalKey;
    12     private int secondKey;
    13     public void set(Text naturalkey,Text secondkey ) {
    14         // TODO 自动生成的构造函数存根
    15         this.naturalKey=Integer.parseInt(naturalkey.toString());
    16         this.secondKey=Integer.parseInt(secondkey.toString());        
    17     }
    18     public int getNaturalKey() {
    19         return naturalKey;
    20     }
    21     @Override
    22     //反序列化
    23     public void readFields(DataInput arg0) throws IOException {
    24         // TODO 自动生成的方法存根
    25         this.naturalKey=arg0.readInt();
    26         this.secondKey=arg0.readInt();
    27         
    28     }
    29     @Override
    30     //序列化
    31     public void write(DataOutput arg0) throws IOException {
    32         // TODO 自动生成的方法存根
    33         arg0.writeInt(naturalKey);
    34         arg0.writeInt(secondKey);
    35     }
    36     @Override
    37     public int compareTo(compositeKey o) {
    38         // TODO 自动生成的方法存根
    39         int flag=this.naturalKey>o.naturalKey?1:(this.naturalKey==o.naturalKey?0:-1);
    40         if(flag!=0)
    41             return flag;
    42         return this.secondKey>o.secondKey?1:(this.secondKey==o.secondKey?0:-1);
    43     }
    44 }

    原以为放在集群上跑,会造成每个reduce中顺序混乱,因为并没有定义分组比较类(setGroupingComparatorClass),目前认为没必要定义这个类,示例代码分析:

     1 public class PersonNameComparator extends WritableComparator {
     2 
     3     protected PersonNameComparator() {
     4         super(Person.class, true);
     5     }
     6     
     7     @Override
     8     public int compare(WritableComparable o1, WritableComparable o2) {
     9         Person p1 = (Person) o1;
    10         Person p2 = (Person) o2;
    11         return p1.getLastName().compareTo(p2.getLastName());
    12     }
    13 }

    虽然是定义了分组排序的类,但是在排序函数compare内部还是调用了自定义的复合键类的方法compareTo方法。这个操作是在reduce端完成的。如下所述:

    当reduce阶段将在本地磁盘上的map输出的记录进行流化处理(streaming)的时候,需要要进行分组。在分组中,记录将被按一定方式排成一个有逻辑顺序的流,并被传输给reduce。在分组阶段,所有的记录已经经过了次排序。

    结果如下所示:

    -------------------

    -------------------------------------------------

    -------------------------------------

    -

    ---------------------------------------------------

    谢谢:

    http://www.cnblogs.com/datacloud/p/3584640.html

    http://my.oschina.net/leejun2005/blog/132785

    http://padma0.tumblr.com/post/59931427752/mapreduce%E7%9A%84%E4%BA%8C%E6%AC%A1%E6%8E%92%E5%BA%8F%E5%8E%9F%E7%90%86

  • 相关阅读:
    VMware下Linux配置网络
    前端知识之查漏补缺二
    前端网络基础查漏补缺篇
    简单实现Promise
    前端知识之查漏补缺-1
    git tag
    云服务器安装node环境 mysql nginx
    js动画
    vue原理之双向绑定虚
    js的封装、继承与多态
  • 原文地址:https://www.cnblogs.com/lz3018/p/4965087.html
Copyright © 2011-2022 走看看