• 6.命令行编译打包运行五个MapReduce程序


    对于如何编译WordCount.java,对于0.20 等旧版本版本的做法很常见,具体如下:

    javac -classpath /usr/local/hadoop/hadoop-1.0.1/hadoop-core-1.0.1.jar WordCount.java

    但较新的 2.X 版本中,已经没有 hadoop-core*.jar 这个文件,因此编辑和打包自己的MapReduce程序与旧版本有所不同。

    Hadoop 2.x 版本中的依赖 jar

    Hadoop 2.x 版本中jar不再集中在一个 hadoop-core*.jar 中,而是分成多个 jar,如运行WordCount实例需要如下三个 jar:

    • $HADOOP_HOME/share/hadoop/common/hadoop-common-2.x.x.jar

    • $HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-client-core-2.x.x.jar

    • $HADOOP_HOME/share/hadoop/common/lib/commons-cli-1.2.jar

    例1.wordcount.java如下:

      1 import java.io.IOException; 
      2 import java.util.StringTokenizer;  
      3 import org.apache.hadoop.conf.Configuration;  
      4 import org.apache.hadoop.fs.Path;  
      5 import org.apache.hadoop.io.IntWritable;  
      6 import org.apache.hadoop.io.Text;  
      7 import org.apache.hadoop.mapred.JobConf;  
      8 import org.apache.hadoop.mapreduce.Job;  
      9 import org.apache.hadoop.mapreduce.Mapper; 
     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.util.GenericOptionsParser; 
     14 public class WordCount {
     15 /**  
     16 * MapReduceBase类:实现了Mapper和Reducer接口的基类(其中的方法只是实现接口,而未作任何事情)  
     17 * Mapper接口:
     18 * WritableComparable接口:实现WritableComparable的类可以相互比较。所有被用作key的类应该实现此接口。  
     19     * Reporter 则可用于报告整个应用的运行进度,本例中未使用。   
     20     *   
     21     */    
     22  public static class TokenizerMapper   
     23       extends Mapper<Object, Text, Text, IntWritable>{  
     24      /**  
     25       * LongWritable, IntWritable, Text 均是 Hadoop 中实现的用于封装 Java 数据类型的类,这些类实现了WritableComparable接口,  
     26       * 都能够被串行化从而便于在分布式环境中进行数据交换,你可以将它们分别视为long,int,String 的替代品。  
     27       */   
     28    private final static IntWritable one = new IntWritable(1);  
     29    private Text word = new Text();//Text 实现了BinaryComparable类可以作为key值  
     30    /**  
     31     * Mapper接口中的map方法:  
     32     * void map(K1 key, V1 value, OutputCollector<K2,V2> output, Reporter reporter)  
     33     * 映射一个单个的输入k/v对到一个中间的k/v对  
     34     * 输出对不需要和输入对是相同的类型,输入对可以映射到0个或多个输出对。  
     35     * OutputCollector接口:收集Mapper和Reducer输出的<k,v>对。  
     36     * OutputCollector接口的collect(k, v)方法:增加一个(k,v)对到output  
     37     */    
     38      public void map(Object key, Text value, Context context) throws IOException, InterruptedException {  
     39        /** 
     40         * 原始数据: 
     41         * c++ java hello 
     42            world java hello 
     43            you me too 
     44            map阶段,数据如下形式作为map的输入值:key为偏移量 
     45            0  c++ java hello 
     46            16 world java hello 
     47            34 you me too 
     48             
     49         */  
     50         /** 
     51          * 以下解析键值对 
     52         * 解析后以键值对格式形成输出数据 
     53         * 格式如下:前者是键排好序的,后者数字是值 
     54         * c++ 1 
     55         * java 1 
     56         * hello 1 
     57         * world 1 
     58         * java 1 
     59         * hello 1 
     60         * you 1 
     61         * me 1 
     62         * too 1 
     63         * 这些数据作为reduce的输出数据 
     64         */  
     65      StringTokenizer itr = new StringTokenizer(value.toString());//得到什么值  
     66 //     System.out.println("value什么东西 : "+value.toString());  
     67 //     System.out.println("key什么东西 : "+key.toString());  
     68      while (itr.hasMoreTokens()) {  
     69        word.set(itr.nextToken());  
     70        context.write(word, one);  
     71      }  
     72        }  
     73    }
     74  public static class IntSumReducer extends Reducer<Text,IntWritable,Text,IntWritable> {  
     75         private IntWritable result = new IntWritable();  
     76         /** 
     77          * reduce过程是对输入数据解析形成如下格式数据: 
     78          * (c++ [1]) 
     79          * (java [1,1]) 
     80          * (hello [1,1]) 
     81          * (world [1]) 
     82          * (you [1]) 
     83          * (me [1]) 
     84          * (you [1]) 
     85          * 供接下来的实现的reduce程序分析数据数据 
     86          *  
     87          */  
     88         public void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {  
     89           int sum = 0;  
     90           /** 
     91            * 自己的实现的reduce方法分析输入数据 
     92            * 形成数据格式如下并存储 
     93            *     c++    1 
     94            *    hello   2 
     95            *    java    2 
     96            *    me      1 
     97            *    too     1 
     98            *    world   1 
     99            *    you     1 
    100            *     
    101            */  
    102           for (IntWritable val : values) {  
    103             sum += val.get();  
    104           }  
    105            
    106           result.set(sum);  
    107           context.write(key, result);  
    108         }  
    109       }  
    110 public static void main(String[] args) throws Exception {
    111     Configuration conf = new Configuration();
    112     String[] otherArgs =new GenericOptionsParser(conf,args).getRemainingArgs();
    113     if(otherArgs.length!=2){
    114         System.err.println("Usage:wordcount <in><out>");
    115         System.exit(2);
    116     }
    117     Job job= new Job (conf ,"word count");
    118     job.setJarByClass(WordCount.class);  
    119    job.setMapperClass(TokenizerMapper.class); //为job设置Mapper类   
    120    job.setCombinerClass(IntSumReducer.class); //为job设置Combiner类    
    121    job.setReducerClass(IntSumReducer.class); //为job设置Reduce类     
    122    job.setOutputKeyClass(Text.class);        //设置输出key的类型  
    123    job.setOutputValueClass(IntWritable.class);//  设置输出value的类型  
    124    FileInputFormat.addInputPath(job, new Path(otherArgs[0])); //为map-reduce任务设置InputFormat实现类   设置输入路径  
    125      
    126    FileOutputFormat.setOutputPath(job, new Path(otherArgs[1]));//为map-reduce任务设置OutputFormat实现类  设置输出路径  
    127    System.exit(job.waitForCompletion(true) ? 0 : 1);  
    128  }  
    129 }

    以Hadoop2.2.0为例,编译wordcount的命令为(要先建一个WordCount文件夹):

    root@master:/usr/local/hadoop/hadoop-2.2.0# javac -classpath share/hadoop/common/hadoop-common-2.2.0.jar:share/hadoop/mapreduce/hadoop-mapreduce-client-core-2.2.0.jar:share/hadoop/common/lib/commons-cli-1.2.jar -d WordCount WordCount.java

    打包的命令为:

    root@master:/usr/local/hadoop/hadoop-2.2.0# jar -cvf wordcount.jar -C WordCount .

    打包后即可在hadoop上运行程序

    root@master:/usr/local/hadoop/hadoop-2.2.0# hadoop jar wordcount.jar WordCount /input /output

     

    例二.去重程序

    样例输入如下所示:

         1)file1:

    2012-3-1 a

    2012-3-2 b

    2012-3-3 c

    2012-3-4 d

    2012-3-5 a

    2012-3-6 b

    2012-3-7 c

    2012-3-3 c

         2)file2:

    2012-3-1 b

    2012-3-2 a

    2012-3-3 b

    2012-3-4 d

    2012-3-5 a

    2012-3-6 c

    2012-3-7 d

    2012-3-3 c

         样例输出如下所示:

    2012-3-1 a

    2012-3-1 b

    2012-3-2 a

    2012-3-2 b

    2012-3-3 b

    2012-3-3 c

    2012-3-4 d

    2012-3-5 a

    2012-3-6 b

    2012-3-6 c

    2012-3-7 c

    2012-3-7 d

    dedup.java代码如下:

      1 import java.io.IOException;
      2 
      3  
      4 
      5 import org.apache.hadoop.conf.Configuration;
      6 
      7 import org.apache.hadoop.fs.Path;
      8 
      9 import org.apache.hadoop.io.IntWritable;
     10 
     11 import org.apache.hadoop.io.Text;
     12 
     13 import org.apache.hadoop.mapreduce.Job;
     14 
     15 import org.apache.hadoop.mapreduce.Mapper;
     16 
     17 import org.apache.hadoop.mapreduce.Reducer;
     18 
     19 import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
     20 
     21 import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
     22 
     23 import org.apache.hadoop.util.GenericOptionsParser;
     24 
     25  
     26 
     27 public class dedup {
     28 
     29  
     30 
     31     //map将输入中的value复制到输出数据的key上,并直接输出
     32 
     33     public static class Map extends Mapper<Object,Text,Text,Text>{
     34 
     35         private static Text line=new Text();//每行数据
     36 
     37        
     38 
     39         //实现map函数
     40 
     41         public void map(Object key,Text value,Context context)
     42 
     43                 throws IOException,InterruptedException{
     44 
     45             line=value;
     46 
     47             context.write(line, new Text(""));
     48 
     49         }
     50 
     51        
     52 
     53     }
     54 
     55    
     56 
     57     //reduce将输入中的key复制到输出数据的key上,并直接输出
     58 
     59     public static class Reduce extends Reducer<Text,Text,Text,Text>{
     60 
     61         //实现reduce函数
     62 
     63         public void reduce(Text key,Iterable<Text> values,Context context)
     64 
     65                 throws IOException,InterruptedException{
     66 
     67             context.write(key, new Text(""));
     68 
     69         }
     70 
     71        
     72 
     73     }
     74 
     75    
     76 
     77     public static void main(String[] args) throws Exception{
     78 
     79         Configuration conf = new Configuration();
     80 
     81 
     82      String[] otherArgs = new GenericOptionsParser(conf, args).getRemainingArgs();
     83 
     84      if (otherArgs.length != 2) {
     85 
     86      System.err.println("Usage: Data Deduplication <in> <out>");
     87 
     88      System.exit(2);
     89 
     90      }
     91 
     92      
     93 
     94      Job job = new Job(conf, "Data Deduplication");
     95 
     96      job.setJarByClass(dedup.class);
     97 
     98      
     99 
    100      //设置Map、Combine和Reduce处理类
    101 
    102      job.setMapperClass(Map.class);
    103 
    104      job.setCombinerClass(Reduce.class);
    105 
    106      job.setReducerClass(Reduce.class);
    107 
    108      
    109 
    110      //设置输出类型
    111 
    112      job.setOutputKeyClass(Text.class);
    113 
    114      job.setOutputValueClass(Text.class);
    115 
    116      
    117 
    118      //设置输入和输出目录
    119 
    120      FileInputFormat.addInputPath(job, new Path(otherArgs[0]));
    121 
    122      FileOutputFormat.setOutputPath(job, new Path(otherArgs[1]));
    123 
    124      System.exit(job.waitForCompletion(true) ? 0 : 1);
    125 
    126      }
    127 
    128 } 

    编译过程:

    root@master:/usr/local/hadoop/hadoop-2.2.0# javac -classpath share/hadoop/common/hadoop-common-2.2.0.jar:share/hadoop/mapreduce/hadoop-mapreduce-client-core-2.2.0.jar:share/hadoop/common/lib/commons-cli-1.2.jar -d dedup dedup.java

    打包:

    root@master:/usr/local/hadoop/hadoop-2.2.0# jar -cvf dedup.jar -C dedup . 

    运行:

    root@master:/usr/local/hadoop/hadoop-2.2.0# hadoop jar dedup.jar dedup /input/dedup /output/dedup


    也可在eclipse上运行这个程序,代码稍作改动即可。

      1 package quchong;
      2 
      3 import java.io.IOException;
      4 
      5 
      6 
      7 import org.apache.hadoop.conf.Configuration;
      8 
      9 import org.apache.hadoop.fs.Path;
     10 
     11 import org.apache.hadoop.io.IntWritable;
     12 
     13 import org.apache.hadoop.io.Text;
     14 
     15 import org.apache.hadoop.mapreduce.Job;
     16 
     17 import org.apache.hadoop.mapreduce.Mapper;
     18 
     19 import org.apache.hadoop.mapreduce.Reducer;
     20 
     21 import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
     22 
     23 import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
     24 
     25 import org.apache.hadoop.util.GenericOptionsParser;
     26 
     27  
     28 
     29 public class dedup {
     30 
     31  
     32 
     33     //map将输入中的value复制到输出数据的key上,并直接输出
     34 
     35     public static class Map extends Mapper<Object,Text,Text,Text>{
     36 
     37         private static Text line=new Text();//每行数据
     38 
     39        
     40 
     41         //实现map函数
     42 
     43         public void map(Object key,Text value,Context context)
     44 
     45                 throws IOException,InterruptedException{
     46 
     47             line=value;
     48 
     49             context.write(line, new Text(""));
     50 
     51         }
     52 
     53        
     54 
     55     }
     56 
     57    
     58 
     59     //reduce将输入中的key复制到输出数据的key上,并直接输出
     60 
     61     public static class Reduce extends Reducer<Text,Text,Text,Text>{
     62 
     63         //实现reduce函数
     64 
     65         public void reduce(Text key,Iterable<Text> values,Context context)
     66 
     67                 throws IOException,InterruptedException{
     68 
     69             context.write(key, new Text(""));
     70 
     71         }
     72 
     73        
     74 
     75     }
     76 
     77    
     78 
     79     public static void main(String[] args) throws Exception{
     80 
     81         Configuration conf = new Configuration();
     82 
     83  //       conf.set("mapred.job.tracker", "192.168.1.2:9001");
     84 //没有设置Map/Reduce Location的话加上上面那句代码即可。
     85  
     86         String[] ioArgs=new String[]{"hdfs://192.168.10.128:9000/input/dedup","hdfs://192.168.10.128:9000/output/dedup3"};
     87 
     88      String[] otherArgs = new GenericOptionsParser(conf, ioArgs).getRemainingArgs();
     89 
     90      if (otherArgs.length != 2) {
     91 
     92      System.err.println("Usage: Data Deduplication <in> <out>");
     93 
     94      System.exit(2);
     95 
     96      }
     97 
     98      
     99 
    100      Job job = new Job(conf, "Data Deduplication");
    101 
    102      job.setJarByClass(dedup.class);
    103 
    104      
    105 
    106      //设置Map、Combine和Reduce处理类
    107 
    108      job.setMapperClass(Map.class);
    109 
    110      job.setCombinerClass(Reduce.class);
    111 
    112      job.setReducerClass(Reduce.class);
    113 
    114      
    115 
    116      //设置输出类型
    117 
    118      job.setOutputKeyClass(Text.class);
    119 
    120      job.setOutputValueClass(Text.class);
    121 
    122      
    123 
    124      //设置输入和输出目录
    125 
    126      FileInputFormat.addInputPath(job, new Path(otherArgs[0]));
    127 
    128      FileOutputFormat.setOutputPath(job, new Path(otherArgs[1]));
    129 
    130      System.exit(job.waitForCompletion(true) ? 0 : 1);
    131 
    132      }
    133 
    134 } 

    在eclipse编译的过程就不用说了,有问题可以看我前面几篇博客。

    例3.排序

    对输入文件中数据进行排序。输入文件中的每行内容均为一个数字,即一个数据。要求在输出中每行有两个间隔的数字,其中,第一个代表原始数据在原始数据集中的位次,第二个代表原始数据。

        样例输入:

        1)file1:

    2

    32

    654

    32

    15

    756

    65223

        2)file2:

    5956

    22

    650

    92

        3)file3:

    26

    54

    6

        样例输出:

    1    2

    2    6

    3    15

    4    22

    5    26

    6    32

    7    32

    8    54

    9    92

    10    650

    11    654

    12    756

    13    5956

    14    65223

    代码如下:

    import java.io.IOException;
    
     
    
    import org.apache.hadoop.conf.Configuration;
    
    import org.apache.hadoop.fs.Path;
    
    import org.apache.hadoop.io.IntWritable;
    
    import org.apache.hadoop.io.Text;
    
    import org.apache.hadoop.mapreduce.Job;
    
    import org.apache.hadoop.mapreduce.Mapper;
    
    import org.apache.hadoop.mapreduce.Reducer;
    
    import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
    
    import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
    
    import org.apache.hadoop.util.GenericOptionsParser;
    
     
    
    public class Sort {
    
     
    
        //map将输入中的value化成IntWritable类型,作为输出的key
    
        public static class Map extends
    
            Mapper<Object,Text,IntWritable,IntWritable>{
    
            private static IntWritable data=new IntWritable();
    
           
    
            //实现map函数
    
            public void map(Object key,Text value,Context context)
    
                    throws IOException,InterruptedException{
    
                String line=value.toString();
    
                data.set(Integer.parseInt(line));
    
                context.write(data, new IntWritable(1));
    
            }
    
           
    
        }
    
       
    
        //reduce将输入中的key复制到输出数据的key上,
    
        //然后根据输入的value-list中元素的个数决定key的输出次数
    
        //用全局linenum来代表key的位次
    
        public static class Reduce extends
    
                Reducer<IntWritable,IntWritable,IntWritable,IntWritable>{
    
           
    
            private static IntWritable linenum = new IntWritable(1);
    
           
    
            //实现reduce函数
    
            public void reduce(IntWritable key,Iterable<IntWritable> values,Context context)
    
                    throws IOException,InterruptedException{
    
                for(IntWritable val:values){
    
                    context.write(linenum, key);
    
                    linenum = new IntWritable(linenum.get()+1);
    
                }
    
               
    
            }
    
     
    
        }
    
       
    
        public static void main(String[] args) throws Exception{
    
            Configuration conf = new Configuration();
    
     //     conf.set("mapred.job.tracker", "192.168.1.2:9001");
    
    
         String[] otherArgs = new GenericOptionsParser(conf, args).getRemainingArgs();
    
         if (otherArgs.length != 2) {
    
         System.err.println("Usage: Data Sort <in> <out>");
    
             System.exit(2);
    
         }
    
         
    
         Job job = new Job(conf, "Data Sort");
    
         job.setJarByClass(Sort.class);
    
         
    
         //设置Map和Reduce处理类
    
         job.setMapperClass(Map.class);
    
         job.setReducerClass(Reduce.class);
    
         
    
         //设置输出类型
    
         job.setOutputKeyClass(IntWritable.class);
    
         job.setOutputValueClass(IntWritable.class);
    
         
    
         //设置输入和输出目录
    
         FileInputFormat.addInputPath(job, new Path(otherArgs[0]));
    
         FileOutputFormat.setOutputPath(job, new Path(otherArgs[1]));
    
         System.exit(job.waitForCompletion(true) ? 0 : 1);
    
         }
    
    } 

    编译打包的过程都一样,不再赘述。

    例4.单表关联

    实例中给出child-parent(孩子——父母)表,要求输出grandchild-grandparent(孙子——爷奶)表。

        样例输入如下所示。

        file:

    child        parent

    Tom        Lucy

    Tom        Jack

    Jone        Lucy

    Jone        Jack

    Lucy        Mary

    Lucy        Ben

    Jack        Alice

    Jack        Jesse

    Terry        Alice

    Terry        Jesse

    Philip        Terry

    Philip        Alma

    Mark        Terry

    Mark        Alma

    样例输出如下所示。

        file:

    grandchild        grandparent

    Tom              Alice

    Tom              Jesse

    Jone              Alice

    Jone              Jesse

    Tom              Mary

    Tom              Ben

    Jone              Mary

    Jone              Ben

    Philip              Alice

    Philip              Jesse

    Mark              Alice

    Mark              Jesse

      分析这个实例,显然需要进行单表连接,连接的是左表的parent列和右表的child列,且左表和右表是同一个表。

      连接结果中除去连接的两列就是所需要的结果——"grandchild--grandparent"表。要用MapReduce解决这个实例,首先应该考虑如何实现表的自连接;其次就是连接列的设置;最后是结果的整理。

          考虑到MapReduce的shuffle过程会将相同的key会连接在一起,所以可以将map结果的key设置成待连接的列,然后列中相同的值就自然会连接在一起了。再与最开始的分析联系起来:

      要连接的是左表的parent列和右表的child列,且左表和右表是同一个表,所以在map阶段将读入数据分割成child和parent之后,会将parent设置成key,child设置成value进行输出,并作为左表;再将同一对child和parent中的child设置成key,parent设置成value进行输出,作为右表。为了区分输出中的左右表,需要在输出的value中再加上左右表的信息,比如在value的String最开始处加上字符1表示左表,加上字符2表示右表。这样在map的结果中就形成了左表和右表,然后在shuffle过程中完成连接。reduce接收到连接的结果,其中每个key的value-list就包含了"grandchild--grandparent"关系。取出每个key的value-list进行解析,将左表中的child放入一个数组,右表中的parent放入一个数组,然后对两个数组求笛卡尔积就是最后的结果了。

    代码如下:

      1 import java.io.IOException;
      2 
      3 import java.util.*;
      4 
      5  
      6 
      7 import org.apache.hadoop.conf.Configuration;
      8 
      9 import org.apache.hadoop.fs.Path;
     10 
     11 import org.apache.hadoop.io.IntWritable;
     12 
     13 import org.apache.hadoop.io.Text;
     14 
     15 import org.apache.hadoop.mapreduce.Job;
     16 
     17 import org.apache.hadoop.mapreduce.Mapper;
     18 
     19 import org.apache.hadoop.mapreduce.Reducer;
     20 
     21 import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
     22 
     23 import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
     24 
     25 import org.apache.hadoop.util.GenericOptionsParser;
     26 
     27  
     28 
     29 public class STjoin {
     30 
     31  
     32 
     33     public static int time = 0;
     34 
     35  
     36 
     37     /*
     38 
     39      * map将输出分割child和parent,然后正序输出一次作为右表,
     40 
     41      * 反序输出一次作为左表,需要注意的是在输出的value中必须
     42 
     43      * 加上左右表的区别标识。
     44 
     45      */
     46 
     47     public static class Map extends Mapper<Object, Text, Text, Text> {
     48 
     49  
     50 
     51         // 实现map函数
     52 
     53         public void map(Object key, Text value, Context context)
     54 
     55                 throws IOException, InterruptedException {
     56 
     57             String childname = new String();// 孩子名称
     58 
     59             String parentname = new String();// 父母名称
     60 
     61             String relationtype = new String();// 左右表标识
     62 
     63  
     64 
     65             // 输入的一行预处理文本
     66 
     67             StringTokenizer itr=new StringTokenizer(value.toString());
     68 
     69             String[] values=new String[2];
     70 
     71             int i=0;
     72 
     73             while(itr.hasMoreTokens()){
     74 
     75                 values[i]=itr.nextToken();
     76 
     77                 i++;
     78 
     79             }
     80 
     81            
     82 
     83             if (values[0].compareTo("child") != 0) {
     84 
     85                 childname = values[0];
     86 
     87                 parentname = values[1];
     88 
     89  
     90 
     91                 // 输出左表
     92 
     93                 relationtype = "1";
     94 
     95                 context.write(new Text(values[1]), new Text(relationtype +
     96 
     97                         "+"+ childname + "+" + parentname));
     98 
     99  
    100 
    101                 // 输出右表
    102 
    103                 relationtype = "2";
    104 
    105                 context.write(new Text(values[0]), new Text(relationtype +
    106 
    107                         "+"+ childname + "+" + parentname));
    108 
    109             }
    110 
    111         }
    112 
    113  
    114 
    115     }
    116 
    117  
    118 
    119     public static class Reduce extends Reducer<Text, Text, Text, Text> {
    120 
    121  
    122 
    123         // 实现reduce函数
    124 
    125         public void reduce(Text key, Iterable<Text> values, Context context)
    126 
    127                 throws IOException, InterruptedException {
    128 
    129  
    130 
    131             // 输出表头
    132 
    133             if (0 == time) {
    134 
    135                 context.write(new Text("grandchild"), new Text("grandparent"));
    136 
    137                 time++;
    138 
    139             }
    140 
    141  
    142 
    143             int grandchildnum = 0;
    144 
    145             String[] grandchild = new String[10];
    146 
    147             int grandparentnum = 0;
    148 
    149             String[] grandparent = new String[10];
    150 
    151  
    152 
    153             Iterator ite = values.iterator();
    154 
    155             while (ite.hasNext()) {
    156 
    157                 String record = ite.next().toString();
    158 
    159                 int len = record.length();
    160 
    161                 int i = 2;
    162 
    163                 if (0 == len) {
    164 
    165                     continue;
    166 
    167                 }
    168 
    169  
    170 
    171                 // 取得左右表标识
    172 
    173                 char relationtype = record.charAt(0);
    174 
    175                 // 定义孩子和父母变量
    176 
    177                 String childname = new String();
    178 
    179                 String parentname = new String();
    180 
    181  
    182 
    183                 // 获取value-list中value的child
    184 
    185                 while (record.charAt(i) != '+') {
    186 
    187                     childname += record.charAt(i);
    188 
    189                     i++;
    190 
    191                 }
    192 
    193  
    194 
    195                 i = i + 1;
    196 
    197  
    198 
    199                 // 获取value-list中value的parent
    200 
    201                 while (i < len) {
    202 
    203                     parentname += record.charAt(i);
    204 
    205                     i++;
    206 
    207                 }
    208 
    209  
    210 
    211                 // 左表,取出child放入grandchildren
    212 
    213                 if ('1' == relationtype) {
    214 
    215                     grandchild[grandchildnum] = childname;
    216 
    217                     grandchildnum++;
    218 
    219                 }
    220 
    221  
    222 
    223                 // 右表,取出parent放入grandparent
    224 
    225                 if ('2' == relationtype) {
    226 
    227                     grandparent[grandparentnum] = parentname;
    228 
    229                     grandparentnum++;
    230 
    231                 }
    232 
    233             }
    234 
    235  
    236 
    237             // grandchild和grandparent数组求笛卡尔儿积
    238 
    239             if (0 != grandchildnum && 0 != grandparentnum) {
    240 
    241                 for (int m = 0; m < grandchildnum; m++) {
    242 
    243                     for (int n = 0; n < grandparentnum; n++) {
    244 
    245                         // 输出结果
    246 
    247                         context.write(new Text(grandchild[m]), new Text(grandparent[n]));
    248 
    249                     }
    250 
    251                 }
    252 
    253             }
    254 
    255         }
    256 
    257     }
    258 
    259  
    260 
    261     public static void main(String[] args) throws Exception {
    262 
    263         Configuration conf = new Configuration();
    264 
    265 
    266         String[] otherArgs = new GenericOptionsParser(conf, args).getRemainingArgs();
    267 
    268         if (otherArgs.length != 2) {
    269 
    270             System.err.println("Usage: Single Table Join <in> <out>");
    271 
    272             System.exit(2);
    273 
    274         }
    275 
    276  
    277 
    278         Job job = new Job(conf, "Single Table Join");
    279 
    280         job.setJarByClass(STjoin.class);
    281 
    282  
    283 
    284         // 设置Map和Reduce处理类
    285 
    286         job.setMapperClass(Map.class);
    287 
    288         job.setReducerClass(Reduce.class);
    289 
    290  
    291 
    292         // 设置输出类型
    293 
    294         job.setOutputKeyClass(Text.class);
    295 
    296         job.setOutputValueClass(Text.class);
    297 
    298  
    299 
    300         // 设置输入和输出目录
    301 
    302         FileInputFormat.addInputPath(job, new Path(otherArgs[0]));
    303 
    304         FileOutputFormat.setOutputPath(job, new Path(otherArgs[1]));
    305 
    306         System.exit(job.waitForCompletion(true) ? 0 : 1);
    307 
    308     }
    309 
    310 } 

     运行详解(此处图表来自虾皮)

        (1)Map处理:

        map函数输出结果如下所示。

    child        parent                àà                    忽略此行

    Tom        Lucy                   àà                <Lucy,1+Tom+Lucy>

                                                        <Tom,2+Tom+Lucy >

    Tom        Jack                    àà                <Jack,1+Tom+Jack>

                                                        <Tom,2+Tom+Jack>

    Jone        Lucy                 àà                <Lucy,1+Jone+Lucy>

                                                        <Jone,2+Jone+Lucy>

    Jone        Jack                    àà                <Jack,1+Jone+Jack>

                                                        <Jone,2+Jone+Jack>

    Lucy        Mary                   àà                <Mary,1+Lucy+Mary>

                                                        <Lucy,2+Lucy+Mary>

    Lucy        Ben                    àà                <Ben,1+Lucy+Ben>

                                                         <Lucy,2+Lucy+Ben>

    Jack        Alice                    àà                <Alice,1+Jack+Alice>

                                                          <Jack,2+Jack+Alice>

    Jack        Jesse                   àà                <Jesse,1+Jack+Jesse>

                                                          <Jack,2+Jack+Jesse>

    Terry        Alice                   àà                <Alice,1+Terry+Alice>

                                                          <Terry,2+Terry+Alice>

    Terry        Jesse                  àà                <Jesse,1+Terry+Jesse>

                                                          <Terry,2+Terry+Jesse>

    Philip        Terry                  àà                <Terry,1+Philip+Terry>

                                                          <Philip,2+Philip+Terry>

    Philip        Alma                   àà                <Alma,1+Philip+Alma>

                                                          <Philip,2+Philip+Alma>

    Mark        Terry                   àà                <Terry,1+Mark+Terry>

                                                          <Mark,2+Mark+Terry>

    Mark        Alma                 àà                <Alma,1+Mark+Alma>

                                                          <Mark,2+Mark+Alma>

        (2)Shuffle处理

        在shuffle过程中完成连接。

    map函数输出

    排序结果

    shuffle连接

    <Lucy1+Tom+Lucy>

    <Tom2+Tom+Lucy>

    <Jack1+Tom+Jack>

    <Tom2+Tom+Jack>

    <Lucy1+Jone+Lucy>

    <Jone2+Jone+Lucy>

    <Jack1+Jone+Jack>

    <Jone2+Jone+Jack>

    <Mary1+Lucy+Mary>

    <Lucy2+Lucy+Mary>

    <Ben1+Lucy+Ben>

    <Lucy2+Lucy+Ben>

    <Alice1+Jack+Alice>

    <Jack2+Jack+Alice>

    <Jesse1+Jack+Jesse>

    <Jack2+Jack+Jesse>

    <Alice1+Terry+Alice>

    <Terry2+Terry+Alice>

    <Jesse1+Terry+Jesse>

    <Terry2+Terry+Jesse>

    <Terry1+Philip+Terry>

    <Philip2+Philip+Terry>

    <Alma1+Philip+Alma>

    <Philip2+Philip+Alma>

    <Terry1+Mark+Terry>

    <Mark2+Mark+Terry>

    <Alma1+Mark+Alma>

    <Mark2+Mark+Alma>

    <Alice1+Jack+Alice>

    <Alice1+Terry+Alice>

    <Alma1+Philip+Alma>

    <Alma1+Mark+Alma>

    <Ben1+Lucy+Ben>

    <Jack1+Tom+Jack>

    <Jack1+Jone+Jack>

    <Jack2+Jack+Alice>

    <Jack2+Jack+Jesse>

    <Jesse1+Jack+Jesse>

    <Jesse1+Terry+Jesse>

    <Jone2+Jone+Lucy>

    <Jone2+Jone+Jack>

    <Lucy1+Tom+Lucy>

    <Lucy1+Jone+Lucy>

    <Lucy2+Lucy+Mary>

    <Lucy2+Lucy+Ben>

    <Mary1+Lucy+Mary>

    <Mark2+Mark+Terry>

    <Mark2+Mark+Alma>

    <Philip2+Philip+Terry>

    <Philip2+Philip+Alma>

    <Terry2+Terry+Alice>

    <Terry2+Terry+Jesse>

    <Terry1+Philip+Terry>

    <Terry1+Mark+Terry>

    <Tom2+Tom+Lucy>

    <Tom2+Tom+Jack>

    <Alice1+Jack+Alice

            1+Terry+Alice

            1+Philip+Alma

            1+Mark+Alma >

    <Ben1+Lucy+Ben>

    <Jack1+Tom+Jack

            1+Jone+Jack

            2+Jack+Alice

            2+Jack+Jesse >

    <Jesse1+Jack+Jesse

            1+Terry+Jesse >

    <Jone2+Jone+Lucy

            2+Jone+Jack>

    <Lucy1+Tom+Lucy

            1+Jone+Lucy

            2+Lucy+Mary

            2+Lucy+Ben>

    <Mary1+Lucy+Mary

            2+Mark+Terry

            2+Mark+Alma>

    <Philip2+Philip+Terry

            2+Philip+Alma>

    <Terry2+Terry+Alice

            2+Terry+Jesse

            1+Philip+Terry

            1+Mark+Terry>

    <Tom2+Tom+Lucy

            2+Tom+Jack>

        (3)Reduce处理

        首先由语句"0 != grandchildnum && 0 != grandparentnum"得知,只要在"value-list"中没有左表或者右表,则不会做处理,可以根据这条规则去除无效的shuffle连接。

     

    无效shuffle连接

    有效shuffle连接

    <Alice1+Jack+Alice

            1+Terry+Alice

            1+Philip+Alma

            1+Mark+Alma >

    <Ben1+Lucy+Ben>

    <Jesse1+Jack+Jesse

            1+Terry+Jesse >

    <Jone2+Jone+Lucy

            2+Jone+Jack>

    <Mary1+Lucy+Mary

            2+Mark+Terry

            2+Mark+Alma>

    <Philip2+Philip+Terry

            2+Philip+Alma>

    <Tom2+Tom+Lucy

            2+Tom+Jack>

    <Jack1+Tom+Jack

            1+Jone+Jack

            2+Jack+Alice

            2+Jack+Jesse >

    <Lucy1+Tom+Lucy

            1+Jone+Lucy

            2+Lucy+Mary

            2+Lucy+Ben>

    <Terry2+Terry+Alice

            2+Terry+Jesse

            1+Philip+Terry

            1+Mark+Terry>

        然后根据下面语句进一步对有效的shuffle连接做处理。

    // 左表,取出child放入grandchildren

    if ('1' == relationtype) {

        grandchild[grandchildnum] = childname;

        grandchildnum++;

    }

    // 右表,取出parent放入grandparent

    if ('2' == relationtype) {

        grandparent[grandparentnum] = parentname;

        grandparentnum++;

    }

        针对一条数据进行分析:

    <Jack,1+Tom+Jack,

            1+Jone+Jack,

            2+Jack+Alice,

            2+Jack+Jesse >

        分析结果:左表用"字符1"表示,右表用"字符2"表示,上面的<key,value-list>中的"key"表示左表与右表的连接键。而"value-list"表示以"key"连接的左表与右表的相关数据。

        根据上面针对左表与右表不同的处理规则,取得两个数组的数据如下所示:

     

    grandchild

    TomJonegrandchild[grandchildnum] = childname;

    grandparent

    AliceJessegrandparent[grandparentnum] = parentname;

        

        然后根据下面语句进行处理。

    for (int m = 0; m < grandchildnum; m++) {

        for (int n = 0; n < grandparentnum; n++) {

            context.write(new Text(grandchild[m]), new Text(grandparent[n]));

        }

    }

    处理结果如下面所示:

    Tom        Jesse

    Tom        Alice

    Jone        Jesse

    Jone        Alice 

    其他的有效shuffle连接处理都是如此。

    例5.多表关联

    样例输入如下所示。

        1)factory:

    factoryname                    addressed

    Beijing Red Star                    1

    Shenzhen Thunder                3

    Guangzhou Honda                2

    Beijing Rising                       1

    Guangzhou Development Bank      2

    Tencent                        3

    Back of Beijing                     1

     

        2)address:

    addressID    addressname

    1            Beijing

    2            Guangzhou

    3            Shenzhen

    4            Xian

     

        样例输出如下所示。

     

    factoryname                        addressname

    Back of Beijing                          Beijing

    Beijing Red Star                        Beijing

    Beijing Rising                          Beijing

    Guangzhou Development Bank          Guangzhou

    Guangzhou Honda                    Guangzhou

    Shenzhen Thunder                    Shenzhen

    Tencent                            Shenzhen

       多表关联和单表关联相似,都类似于数据库中的自然连接。相比单表关联,多表关联的左右表和连接列更加清楚。所以可以采用和单表关联的相同的处理方式,map识别出输入的行属于哪个表之后,对其进行分割,将连接的列值保存在key中,另一列和左右表标识保存在value中,然后输出。reduce拿到连接结果之后,解析value内容,根据标志将左右表内容分开存放,然后求笛卡尔积,最后直接输出。

    代码如下:

      1 import java.io.IOException;
      2 
      3 import java.util.*;
      4 
      5  
      6 
      7 import org.apache.hadoop.conf.Configuration;
      8 
      9 import org.apache.hadoop.fs.Path;
     10 
     11 import org.apache.hadoop.io.IntWritable;
     12 
     13 import org.apache.hadoop.io.Text;
     14 
     15 import org.apache.hadoop.mapreduce.Job;
     16 
     17 import org.apache.hadoop.mapreduce.Mapper;
     18 
     19 import org.apache.hadoop.mapreduce.Reducer;
     20 
     21 import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
     22 
     23 import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
     24 
     25 import org.apache.hadoop.util.GenericOptionsParser;
     26 
     27  
     28 
     29 public class MTjoin {
     30 
     31  
     32 
     33     public static int time = 0;
     34 
     35  
     36 
     37     /*
     38 
     39      * 在map中先区分输入行属于左表还是右表,然后对两列值进行分割,
     40 
     41      * 保存连接列在key值,剩余列和左右表标志在value中,最后输出
     42 
     43      */
     44 
     45     public static class Map extends Mapper<Object, Text, Text, Text> {
     46 
     47  
     48 
     49         // 实现map函数
     50 
     51         public void map(Object key, Text value, Context context)
     52 
     53                 throws IOException, InterruptedException {
     54 
     55             String line = value.toString();// 每行文件
     56 
     57             String relationtype = new String();// 左右表标识
     58 
     59  
     60 
     61             // 输入文件首行,不处理
     62 
     63             if (line.contains("factoryname") == true
     64 
     65                     || line.contains("addressed") == true) {
     66 
     67                 return;
     68 
     69             }
     70 
     71  
     72 
     73             // 输入的一行预处理文本
     74 
     75             StringTokenizer itr = new StringTokenizer(line);
     76 
     77             String mapkey = new String();
     78 
     79             String mapvalue = new String();
     80 
     81             int i = 0;
     82 
     83             while (itr.hasMoreTokens()) {
     84 
     85                 // 先读取一个单词
     86 
     87                 String token = itr.nextToken();
     88 
     89                 // 判断该地址ID就把存到"values[0]"
     90 
     91                 if (token.charAt(0) >= '0' && token.charAt(0) <= '9') {
     92 
     93                     mapkey = token;
     94 
     95                     if (i > 0) {
     96 
     97                         relationtype = "1";
     98 
     99                     } else {
    100 
    101                         relationtype = "2";
    102 
    103                     }
    104 
    105                     continue;
    106 
    107                 }
    108 
    109  
    110 
    111                 // 存工厂名
    112 
    113                 mapvalue += token + " ";
    114 
    115                 i++;
    116 
    117             }
    118 
    119  
    120 
    121             // 输出左右表
    122 
    123             context.write(new Text(mapkey), new Text(relationtype + "+"+ mapvalue));
    124 
    125         }
    126 
    127     }
    128 
    129  
    130 
    131     /*
    132 
    133      * reduce解析map输出,将value中数据按照左右表分别保存,
    134 
    135   * 然后求出笛卡尔积,并输出。
    136 
    137      */
    138 
    139     public static class Reduce extends Reducer<Text, Text, Text, Text> {
    140 
    141  
    142 
    143         // 实现reduce函数
    144 
    145         public void reduce(Text key, Iterable<Text> values, Context context)
    146 
    147                 throws IOException, InterruptedException {
    148 
    149  
    150 
    151             // 输出表头
    152 
    153             if (0 == time) {
    154 
    155                 context.write(new Text("factoryname"), new Text("addressname"));
    156 
    157                 time++;
    158 
    159             }
    160 
    161  
    162 
    163             int factorynum = 0;
    164 
    165             String[] factory = new String[10];
    166 
    167             int addressnum = 0;
    168 
    169             String[] address = new String[10];
    170 
    171  
    172 
    173             Iterator ite = values.iterator();
    174 
    175             while (ite.hasNext()) {
    176 
    177                 String record = ite.next().toString();
    178 
    179                 int len = record.length();
    180 
    181                 int i = 2;
    182 
    183                 if (0 == len) {
    184 
    185                     continue;
    186 
    187                 }
    188 
    189  
    190 
    191                 // 取得左右表标识
    192 
    193                 char relationtype = record.charAt(0);
    194 
    195  
    196 
    197                 // 左表
    198 
    199                 if ('1' == relationtype) {
    200 
    201                     factory[factorynum] = record.substring(i);
    202 
    203                     factorynum++;
    204 
    205                 }
    206 
    207  
    208 
    209                 // 右表
    210 
    211                 if ('2' == relationtype) {
    212 
    213                     address[addressnum] = record.substring(i);
    214 
    215                     addressnum++;
    216 
    217                 }
    218 
    219             }
    220 
    221  
    222 
    223             // 求笛卡尔积
    224 
    225             if (0 != factorynum && 0 != addressnum) {
    226 
    227                 for (int m = 0; m < factorynum; m++) {
    228 
    229                     for (int n = 0; n < addressnum; n++) {
    230 
    231                         // 输出结果
    232 
    233                         context.write(new Text(factory[m]),
    234 
    235                                 new Text(address[n]));
    236 
    237                     }
    238 
    239                 }
    240 
    241             }
    242 
    243  
    244 
    245         }
    246 
    247     }
    248 
    249  
    250 
    251     public static void main(String[] args) throws Exception {
    252 
    253         Configuration conf = new Configuration();
    254 
    255 //      conf.set("mapred.job.tracker", "192.168.1.2:9001");
    256 
    257         String[] otherArgs = new GenericOptionsParser(conf, args).getRemainingArgs();
    258 
    259         if (otherArgs.length != 2) {
    260 
    261             System.err.println("Usage: Multiple Table Join <in> <out>");
    262 
    263             System.exit(2);
    264 
    265         }
    266 
    267  
    268 
    269         Job job = new Job(conf, "Multiple Table Join");
    270 
    271         job.setJarByClass(MTjoin.class);
    272 
    273  
    274 
    275         // 设置Map和Reduce处理类
    276 
    277         job.setMapperClass(Map.class);
    278 
    279         job.setReducerClass(Reduce.class);
    280 
    281  
    282 
    283         // 设置输出类型
    284 
    285         job.setOutputKeyClass(Text.class);
    286 
    287         job.setOutputValueClass(Text.class);
    288 
    289  
    290 
    291         // 设置输入和输出目录
    292 
    293         FileInputFormat.addInputPath(job, new Path(otherArgs[0]));
    294 
    295         FileOutputFormat.setOutputPath(job, new Path(otherArgs[1]));
    296 
    297         System.exit(job.waitForCompletion(true) ? 0 : 1);
    298 
    299     }
    300 
    301 } 

    注:本博客大部分代码来自《Hadoop实战2》,中国人民大学,陆嘉恒著。

  • 相关阅读:
    smokeping一键安装脚本
    IIS与Apache禁止IP地址直接访问网站
    linux 常用命令
    ELK安装
    MySQL5.7安装
    Centos7 动态创建文件系统
    python学习第十六天 内置函数2,匿名函数
    python学习第十五天 内置函数1
    python学习第十四天 生成器函数进阶 生成器表达式 各种推导式
    python学习第十三天 迭代器 生成器
  • 原文地址:https://www.cnblogs.com/dmyu/p/4684062.html
Copyright © 2020-2023  润新知