MapReduce之Join操作(2)

原文链接:http://bjyjtdj.iteye.com/blog/1453451

上一篇介绍了 Repartition Join 的基本思想,实践出真知,具体的实现中总是存在各种细节问题。下面我们通过具体的源码分析来加深理解。本文分析的是 Hadoop-0.20.2 版本的 datajoin 代码,其它版本也许会有变化,这里暂且不论。

参看源码目录下,共实现有 7 个类,分别是:

  • ArrayListBackIterator.java
  • DataJoinJob.java
  • DataJoinMapperBase.java
  • DataJoinReducerBase.java
  • JobBase.java
  • ResetableIterator.java
  • TaggedMapOutput.java

        源码比较简单,代码量小,下面对一些关键的地方进行分析:前面我们提到了 map 阶段的输出的 key 值的设定;然而在实现中,其value值也是另外一个需要考虑的地方,在不同的 reduce 结点进行 join 操作时,需要知道参与 join 的元组所属的表;解决方法是在 map 输出的 value 值中加入一个标记 (tag) ,例如上一篇例子中两表的tag 可以分别 customer 和 order (注:实际上,在reduce阶段可以直接分析两元组的结构就可以确定数据来源)。这也是 TaggedMapOutput.java 的来历。作为 Hadoop 的中间数据,必须实现 Writable 的方法,如下所示:

Java代码   收藏代码
  1. public abstract class TaggedMapOutput implements Writable {  
  2.     protected Text tag;  
  3.     public TaggedMapOutput() {  
  4.         this.tag = new Text("");  
  5.     }  
  6.     public Text getTag() {  
  7.         return tag;  
  8.     }  
  9.     public void setTag(Text tag) {  
  10.         this.tag = tag;  
  11.     }  
  12.     public abstract Writable getData();    
  13.     public TaggedMapOutput clone(JobConf job) {  
  14.         return (TaggedMapOutput) WritableUtils.clone(this, job);  
  15.     }  
  16. }   

接下来,我们看看 DataJoinMapperBase 中的相关方法

Java代码   收藏代码
  1. protected abstract TaggedMapOutput generateTaggedMapOutput(Object value);  
  2. protected abstract Text generateGroupKey(TaggedMapOutput aRecord);  

以上两个方法需要由子类实现。上一篇文章提到,将两个表的连接键作为 map 输出的 key 值,其中第二个方法所做的就是这件事,生成一个类型为 Text 的 key ,不过这里是将它称作是 GroupKey 而已。因此 map 方法也就比较简单易懂了

Java代码   收藏代码
  1. public void map(Object key, Object value, OutputCollector output,   
  2.                          Reporter reporter) throws IOException {  
  3.     if (this.reporter == null) {  
  4.         this.reporter = reporter;  
  5.     }  
  6.     addLongValue("totalCount"1);  
  7.     TaggedMapOutput aRecord = generateTaggedMapOutput(value);  
  8.     if (aRecord == null) {  
  9.         addLongValue("discardedCount"1);  
  10.         return;  
  11.     }  
  12.     Text groupKey = generateGroupKey(aRecord);  
  13.     if (groupKey == null) {  
  14.         addLongValue("nullGroupKeyCount"1);  
  15.         return;  
  16.     }  
  17.     output.collect(groupKey, aRecord);  
  18.     addLongValue("collectedCount"1);  
  19. }  

说完了 map 操作,接下来就是 reduce 阶段的事情了。参看 DataJoinReducerBase 这个类,其中的 reduce 方法主要部分是:

Java代码   收藏代码
  1. public void reduce(Object key, Iterator values,   
  2.                              OutputCollector output, Reporter reporter) throws IOException {  
  3.   
  4.     if (this.reporter == null) {  
  5.         this.reporter = reporter;  
  6.     }  
  7.   
  8.     SortedMap<Object, ResetableIterator> groups = regroup(key, values, reporter);  
  9.   
  10.      Object[] tags = groups.keySet().toArray();  
  11.   
  12.     ResetableIterator[] groupValues = new ResetableIterator[tags.length];  
  13.   
  14.     for (int i = 0; i < tags.length; i++) {  
  15.         groupValues[i] = groups.get(tags[i]);  
  16.     }  
  17.   
  18.     joinAndCollect(tags, groupValues, key, output, reporter);  
  19.     addLongValue("groupCount"1);  
  20.   
  21.     for (int i = 0; i < tags.length; i++) {  
  22.         groupValues[i].close();  
  23.     }  
  24. }  

其中 groups 数组保存的是 tag 以及它们对应元组的 iterator 。例如 Customer ID 为 3 的数据块所在的 reduce 节点上, tags = {"Custmoers" , "Orders"}, groupValues 则对应 {"3,Jose Madriz,281-330-8004"} 和 {"3,A,12.95,02-Jun-2008","3,D,25.02,22-Jan-2009"} 的 iterator 。归根结底,关于两个元组的 join 操作放在

Java代码   收藏代码
  1. protected abstract TaggedMapOutput combine(Object[] tags, Object[] values);  

该方法由子类实现。

下面附上 《 Hadoop in Action 》中提供的一种实现

Java代码   收藏代码
  1. public class DataJoin extends Confi gured implements Tool {  
  2.       
  3.     public static class MapClass extends DataJoinMapperBase {  
  4.         protected Text generateInputTag(String inputFile) {  
  5.             String datasource = inputFile.split(“-”)[0];  
  6.             return new Text(datasource);  
  7.         }  
  8.         protected Text generateGroupKey(TaggedMapOutput aRecord) {  
  9.             String line = ((Text) aRecord.getData()).toString();  
  10.             String[] tokens = line.split(“,”);  
  11.             String groupKey = tokens[0];  
  12.             return new Text(groupKey);  
  13.         }  
  14.         protected TaggedMapOutput generateTaggedMapOutput(Object value) {  
  15.            TaggedWritable retv = new TaggedWritable((Text) value);  
  16.            retv.setTag(this.inputTag);  
  17.            return retv;  
  18.        }  
  19.     }  
  20.       
  21.     public static class Reduce extends DataJoinReducerBase {  
  22.         protected TaggedMapOutput combine(Object[] tags, Object[] values) {  
  23.             if (tags.length < 2return null;  
  24.             String joinedStr = “”;  
  25.             for (int i=0; i<values.length; i++) {  
  26.                 if (i > 0) joinedStr += “,”;  
  27.                 TaggedWritable tw = (TaggedWritable) values[i];  
  28.                 String line = ((Text) tw.getData()).toString();  
  29.                 String[] tokens = line.split(“,”, 2);  
  30.                 joinedStr += tokens[1];  
  31.             }  
  32.             TaggedWritable retv = new TaggedWritable(new Text(joinedStr));  
  33.             retv.setTag((Text) tags[0]);  
  34.             return retv;  
  35.         }  
  36.     }  
  37.       
  38.     public static class TaggedWritable extends TaggedMapOutput {  
  39.         private Writable data;  
  40.         public TaggedWritable(Writable data) {  
  41.             this.tag = new Text(“”);  
  42.             this.data = data;  
  43.         }  
  44.         public Writable getData() {  
  45.             return data;  
  46.         }  
  47.         public void write(DataOutput out) throws IOException {  
  48.             this.tag.write(out);  
  49.             this.data.write(out);  
  50.         }     
  51.         public void readFields(DataInput in) throws IOException {  
  52.             this.tag.readFields(in);  
  53.             this.data.readFields(in);  
  54.         }  
  55.     }  
  56.       
  57.     public int run(String[] args) throws Exception {  
  58.         Confi guration conf = getConf();  
  59.         JobConf job = new JobConf(conf, DataJoin.class);  
  60.         Path in = new Path(args[0]);  
  61.         Path out = new Path(args[1]);  
  62.         FileInputFormat.setInputPaths(job, in);  
  63.         FileOutputFormat.setOutputPath(job, out);  
  64.         job.setJobName(“DataJoin”);  
  65.         job.setMapperClass(MapClass.class);  
  66.         job.setReducerClass(Reduce.class);  
  67.         job.setInputFormat(TextInputFormat.class);  
  68.         job.setOutputFormat(TextOutputFormat.class);  
  69.         job.setOutputKeyClass(Text.class);  
  70.         job.setOutputValueClass(TaggedWritable.class);  
  71.         job.set(“mapred.textoutputformat.separator”, “,”);  
  72.         JobClient.runJob(job);  
  73.         return 0;  
  74.     }  
  75.       
  76.     public static void main(String[] args) throws Exception {  
  77.         int res = ToolRunner.run(new Confi guration(),  
  78.         new DataJoin(),  
  79.         args);  
  80.         System.exit(res);  
  81.     }  
  82.   
  83. }   

你可能感兴趣的:(JOIN,JOIN,JOIN,mapreduce,mapreduce,hadoop,hadoop,hive,hive,hive)