Hadoop(五)——核心编程MapReduce(下)

来源:互联网 发布:nginx css加载慢 编辑:程序博客网 时间:2024/06/04 01:10
上篇博客最后我们讲述了WordCountHadoop官方源码,主要看map类的编写规则,入参(从文件)出参(经过shuffle,combiner过程给reduce),reduce的编写规则,入参(从map类中获取),出参(想要的结果输出到文件中)。下边我们再进一步通过几个例子(在hadoop实战中摘取),来加深map-reduce的编程规则,至于具体到mapreduce内部,如何处理数据,则涉及到算法,因情况而异。在这里我们学到mapreduce的执行流程,编写规则等即可。

 

   一,数据去重:顾名思义就是计算数据中的类别,将数据多余一次只显示一次。很简单的一个过程,对于mapreduce ,直接看代码,注意里边的注释:


[java] view plain copy print?
  1. <span style="font-family:KaiTi_GB2312;font-size:18px;">package job;  
  2.   
  3. import java.io.IOException;  
  4.   
  5. import org.apache.hadoop.conf.Configuration;  
  6. import org.apache.hadoop.fs.Path;  
  7. import org.apache.hadoop.io.Text;  
  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.   
  15. /** 
  16.  * 一,数据去重的Map-Reduce实例 
  17.  *  
  18.  * @author ljh 
  19.  *  
  20.  */  
  21. public class Dedup {  
  22.   
  23.     /* 
  24.      * 1,Map将输入中的value复制到输出数据的key上,并直接输出,value值无所谓,利用shuffle这个工程进行key相同汇总 
  25.      */  
  26.     public static class Map extends Mapper<Object, Text, Text, Text> {  
  27.         // 定义line存储每行的数据  
  28.         private static Text line = new Text();  
  29.   
  30.         // map函数直接将value复制给line,然后输出即可  
  31.         public void map(Object key, Text value, Context context)  
  32.                 throws IOException, InterruptedException {  
  33.             line = value;  
  34.             context.write(line, new Text(""));  
  35.         }  
  36.     }  
  37.   
  38.     /* 
  39.      * 2,reduce将输入的key复制到输出数据的key上,并直接输出 
  40.      */  
  41.     public static class Reduce extends Reducer<Text, Text, Text, Text> {  
  42.         // reduce函数,利用shuffle处理好的,直接输出即可,比较简单  
  43.         public void reduce(Text key, Text values, Context context)  
  44.                 throws IOException, InterruptedException {  
  45.             context.write(key, new Text(""));  
  46.         }  
  47.     }  
  48.   
  49.     /* 
  50.      * 3,main方法 
  51.      */  
  52.     public static void main(String[] args) throws Exception {  
  53.         Configuration conf = new Configuration();  
  54.         // 获取输入文件和输出文件的地址  
  55.         String[] otherArgs = new GenericOptionsParser(conf, args)  
  56.                 .getRemainingArgs();  
  57.         if (otherArgs.length != 2) {  
  58.             System.err.println("Usage:  in and out");  
  59.             System.exit(2);  
  60.         }  
  61.   
  62.         Job job = new Job(conf, "Data deduplication");  
  63.         job.setJarByClass(Dedup.class);  
  64.         job.setMapperClass(Map.class);  
  65.         job.setCombinerClass(Reduce.class);  
  66.         job.setReducerClass(Reduce.class);  
  67.         job.setOutputKeyClass(Text.class);  
  68.         job.setOutputValueClass(Text.class);  
  69.         FileInputFormat.addInputPath(job, new Path(otherArgs[0]));  
  70.         FileOutputFormat.setOutputPath(job, new Path(otherArgs[1]));  
  71.         System.exit(job.waitForCompletion(true) ? 0 : 1);  
  72.   
  73.     }  
  74. }  



    二,排序:这个在我们写SQL中利用order by列名desc/asc进行排序即可,Java中我们有基本的冒泡排序,选择排序,顺序排序等,还有堆排序,归并排序,基数排序等,这里看两篇文章:http://blog.csdn.NET/ygc87/article/details/7208082 http://www.cnblogs.com/liuling/p/2013-7-24-01.html  。而在mapreduce中就有默认的排序,如果key封装的intIntWritable,则会按照数字的大小进行排序;如果封装的StringText,则会按照字典的顺序字符串进行排序。好,看下边的程序代码:


[java] view plain copy print?
  1. <span style="font-family:KaiTi_GB2312;font-size:18px;">package job;  
  2.   
  3. import java.io.IOException;  
  4.   
  5. import org.apache.hadoop.conf.Configuration;  
  6. import org.apache.hadoop.fs.Path;  
  7. import org.apache.hadoop.io.IntWritable;  
  8. import org.apache.hadoop.io.Text;  
  9. import org.apache.hadoop.mapreduce.Job;  
  10. import org.apache.hadoop.mapreduce.Mapper;  
  11. import org.apache.hadoop.mapreduce.Reducer;  
  12. import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;  
  13. import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;  
  14. import org.apache.hadoop.util.GenericOptionsParser;  
  15.   
  16. /** 
  17.  * 二,排序的Map-Reduce实例:排序为一些数字 
  18.  * @author ljh 
  19.  * 
  20.  */  
  21. public class Sort {  
  22.     /* 
  23.      * 1,map将输入中的value化成IntWritable类型,作为输出的key 
  24.      */  
  25.     public static class Map extends Mapper<Object, Text, IntWritable, IntWritable>{  
  26.         //用来存储数据,由于是数字的排序,直接使用IntWritable即可  
  27.         private static IntWritable data=new IntWritable();  
  28.           
  29.         public void map(Object key,Text value,Context context)throws IOException,InterruptedException{  
  30.             String line=value.toString();  
  31.             data.set(Integer.parseInt(line));  
  32.             context.write(data, new IntWritable(1));  
  33.         }  
  34.     }  
  35.       
  36.     public static class Reduce extends Reducer<IntWritable, IntWritable, IntWritable, IntWritable>{  
  37.         //第一行的行号设置为1  
  38.         private static IntWritable linenum=new IntWritable(1);  
  39.           
  40.         //已经自动排序好了,输出即可,这里我们来设置一下行号自动加1即可  
  41.         public void reduce(IntWritable key, Iterable<IntWritable> values, Context context) throws IOException,InterruptedException{  
  42.             for(IntWritable val:values){  
  43.                 context.write(linenum, key);  
  44.                 linenum=new IntWritable(linenum.get()+1);  
  45.             }     
  46.         }  
  47.     }  
  48.       
  49.     /* 
  50.      * 3,main方法 
  51.      */  
  52.     public static void main(String[] args) throws Exception {  
  53.         Configuration conf = new Configuration();  
  54.         // 获取输入文件和输出文件的地址  
  55.         String[] otherArgs = new GenericOptionsParser(conf, args)  
  56.                 .getRemainingArgs();  
  57.         if (otherArgs.length != 2) {  
  58.             System.err.println("Usage:  in and out");  
  59.             System.exit(2);  
  60.         }  
  61.   
  62.         Job job = new Job(conf, "Data Sort");  
  63.         job.setJarByClass(Sort.class);  
  64.         job.setMapperClass(Map.class);  
  65.         //这里没有设置CombinerClass,CombinerClass是为了简述网络流量,为了是输出的数据只是必要的,例如上边的去重,先在本地进行去重,再进行传输整合,而这个例子进行的排序是没有必要的。  
  66.         //job.setCombinerClass(Reduce.class);  
  67.         job.setReducerClass(Reduce.class);  
  68.         job.setOutputKeyClass(IntWritable.class);  
  69.         job.setOutputValueClass(IntWritable.class);  
  70.         FileInputFormat.addInputPath(job, new Path(otherArgs[0]));  
  71.         FileOutputFormat.setOutputPath(job, new Path(otherArgs[1]));  
  72.         System.exit(job.waitForCompletion(true) ? 0 : 1);  
  73.   
  74.     }  
  75. }  



    三,单表关联:也就是我们经常见到的一张表中的父子关系,有其树形结构。在orcle中我们有经典的sqlselect * from tb_menu m start with m.id=1 connect by m.parent=prior m.id;

Java中我们使用递归操作来进行操作。下边通过child-parent表,来寻找grandchild-grandparent关系表,也就是通过父子关系数据,找出爷孙关系的数据对应,好,看下Map-reduce中操作:


[java] view plain copy print?
  1. <span style="font-family:KaiTi_GB2312;font-size:18px;">package job;  
  2.   
  3. import java.io.IOException;  
  4. import java.util.Iterator;  
  5.   
  6. import org.apache.hadoop.conf.Configuration;  
  7. import org.apache.hadoop.fs.Path;  
  8. import org.apache.hadoop.io.Text;  
  9. import org.apache.hadoop.mapreduce.Job;  
  10. import org.apache.hadoop.mapreduce.Mapper;  
  11. import org.apache.hadoop.mapreduce.Reducer;  
  12. import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;  
  13. import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;  
  14. import org.apache.hadoop.util.GenericOptionsParser;  
  15.   
  16. /** 
  17.  * 三,单表关联查询父子关系的Map-Reduce实例:父子关系的表数据 
  18.  *  
  19.  * @author ljh 
  20.  *  
  21.  */  
  22. public class STJoin {  
  23.     // 判断是否是首行,因为首行是标题:输入文件为child-parent,输出文件为grandchild-grandparent  
  24.     public static int time = 0;  
  25.   
  26.     /* 
  27.      * 1,map将输入分割为child和parent,然后正序输出依次作为右表,反序依次输出作为左表,需要注意在value中加上左表和右表的标识 
  28.      */  
  29.     public static class Map extends Mapper<Object, Text, Text, Text> {  
  30.   
  31.         public void map(Object key, Text value, Context context)  
  32.                 throws IOException, InterruptedException {  
  33.             String childName = new String();// 孩子的名称存储  
  34.             String parentName = new String();// 父亲的名称存储  
  35.             String relationType = new String();// 左表右表的标识1,表示左表,2表示右表  
  36.             String line = value.toString();// 输入的每一行转化为字符串  
  37.               
  38.             int i = 0;  
  39.             //当检索line的第i个字符不为tab制表符使,进行++,也就是为了下边的切割  
  40.             while (line.charAt(i) != '  ') {  
  41.                 i++;  
  42.             }  
  43.               
  44.             //将child和parent放到数组当中,利用上边找到的分割符  
  45.             String[] values = { line.substring(0, i), line.substring(i + 1) };  
  46.   
  47.             //当不是第一行标题的时候  
  48.             if (values[0].compareTo("child") != 0) {  
  49.                 //获取child和parent  
  50.                 childName = values[0];  
  51.                 parentName = values[1];  
  52.                 // 左表,输出,左右表的key是一对父子关系  
  53.                 relationType = "1";  
  54.                 context.write(new Text(values[1]), new Text(relationType + "+"  
  55.                         + childName + "+" + parentName));  
  56.   
  57.                 // 右表,输出  
  58.                 relationType = "2";  
  59.                 context.write(new Text(values[0]), new Text(relationType + "+"  
  60.                         + childName + "+" + parentName));  
  61.             }  
  62.         }  
  63.     }  
  64.   
  65.     /* 
  66.      * 2,reduce:拿到map输出的左右表是一对负责关系表 
  67.      * 左表:{tom,[{1,lucy,tom},{1,lili,tom}] 
  68.      * 右表:{tom,[{2,tom,hali},{2,tom,karry}] 
  69.      * 其实应该是合并好的:{tom,[{1,lucy,tom},{1,lili,tom},{2,tom,hali},{2,tom,karry}] 
  70.      * 所有,我们取tom的父亲,和tom的孩子,就得到了爷孙关系 
  71.      */  
  72.     public static class Reduce extends Reducer<Text, Text, Text, Text> {  
  73.           
  74.         public void reduce(Text key, Iterable<Text> values, Context context)  
  75.                 throws IOException, InterruptedException {  
  76.             //如果是第一行,则输出表头信息  
  77.             if (time == 0) {  
  78.                 context.write(new Text("grandchild"), new Text("grandparent"));  
  79.                 time++;  
  80.             }  
  81.   
  82.             int grandchildnum = 0;//表示孙子的个数  
  83.             String grandchild[] = new String[10];//存放孙子的数组  
  84.             int grandparentnum = 0;//表示爷爷的个数  
  85.             String grandparent[] = new String[10];//存放爷爷的数组  
  86.             Iterator ite = values.iterator();//每一个儿子对应的父亲列表  
  87.               
  88.             //如果有父亲  
  89.             while (ite.hasNext()) {  
  90.                 //取出父亲的集合  
  91.                 String record = ite.next().toString();  
  92.                 //父亲集合的长度  
  93.                 int len = record.length();  
  94.                 int i = 2;  
  95.                 if (len == 0continue;//结束本次循环  
  96.                   
  97.                 char relationType = record.charAt(0);//取出是父表还是子表  
  98.                 String childname = new String();  
  99.                 String parentname = new String();  
  100.                 //获取value-list中的value的child  
  101.                 while (record.charAt(i) != '+') {  
  102.                     childname = childname + record.charAt(i);  
  103.                     i++;  
  104.                 }  
  105.                 i = i + 1;  
  106.   
  107.                 // 获取value-list中的value的parent  
  108.                 while (i < len) {  
  109.                     parentname = parentname + record.charAt(i);  
  110.                     i++;  
  111.                 }  
  112.                 //如果是父表,取出孩子,如果是子表,取出父亲,组成了爷孙关系  
  113.                 if (relationType == '1') {  
  114.                     grandchild[grandchildnum] = childname;  
  115.                     grandchildnum++;  
  116.                 } else {  
  117.                     grandparent[grandparentnum] = parentname;  
  118.                     grandparentnum++;  
  119.                 }  
  120.             }  
  121.             //爷孙求笛卡尔积,看有多少对组合  
  122.             if (grandparentnum != 0 && grandchildnum != 0) {  
  123.                 for (int m = 0; m < grandchildnum; m++) {  
  124.                     for (int n = 0; n < grandparentnum; n++) {  
  125.                         context.write(new Text(grandchild[m]), new Text(  
  126.                                 grandparent[n]));  
  127.                     }  
  128.                 }  
  129.             }  
  130.         }  
  131.     }  
  132.   
  133.     /* 
  134.      * 3,main方法 
  135.      */  
  136.     public static void main(String[] args) throws Exception {  
  137.         Configuration conf = new Configuration();  
  138.         // 获取输入文件和输出文件的地址  
  139.         String[] otherArgs = new GenericOptionsParser(conf, args)  
  140.                 .getRemainingArgs();  
  141.         if (otherArgs.length != 2) {  
  142.             System.err.println("Usage:  in and out");  
  143.             System.exit(2);  
  144.         }  
  145.   
  146.         Job job = new Job(conf, "single table join");  
  147.         job.setJarByClass(STJoin.class);  
  148.         job.setMapperClass(Map.class);  
  149.         // job.setCombinerClass(Reduce.class);  
  150.         job.setReducerClass(Reduce.class);  
  151.         job.setOutputKeyClass(Text.class);  
  152.         job.setOutputValueClass(Text.class);  
  153.         FileInputFormat.addInputPath(job, new Path(otherArgs[0]));  
  154.         FileOutputFormat.setOutputPath(job, new Path(otherArgs[1]));  
  155.         System.exit(job.waitForCompletion(true) ? 0 : 1);  
  156.   
  157.     }  
  158. }  

   四,多表关联:其实上边的单表关联,我们就是将其转化为两张表进行关联的,其中的关键就是将两表中公用的字段设置为key,我们就能得到关于这个key的集合,其中有左表的数据,有右表的数据,然后我们取出来进行笛卡尔积即可。这里不再给出代码,大家好好思考一下,其实多表关联,比单表关联还简单,因为多表不需要我们进行抽象了,单表还需要我们抽象出两张表。

 


    好,这篇简单讲述了map-reduce的几个例子,虽然简单,但是确实不得不走的路程,慢慢熟悉编写map-reduce的基本知识,然后通过场景,通过各种算法,通过灵活的大脑,来写出属于实际场景有价值的map-reduce来。

0 0
原创粉丝点击