MapReuce-Join操作-初级优化

友情提示:更多有关大数据、人工智能方面技术文章请关注博主个人微信公众号:高级大数据架构师

在上一篇《MapReduce-Join操作-初体验》的结论中说明了上述join方法的不足之处,这一篇中将说明针对上一篇的几个缺陷进行一些一些改进,主要是针对上一篇提到几点:
1.效率低是因为在reduce端遍历了两次集合
2.资源的消耗大是因为重新创建了List来放几乎所有的迭代器中的数据
3.不能适用于所有的业务是因为正式环境往往一个reduce的迭代器中的数据量巨大,而List的最大值为Integer.MAX_VALUE,所以 在数据量巨大的时候,会造成List越界的错误
针对这三个问题的分析可知道,这都是因为在reduce阶段需要先遍历迭代器来找出关于phone的那一个对象,然后再遍历一次进行join的拼接。针对这一问题:那么我们能不能把phone的直接放到第一位那就不用第一次的遍历了,而是改为直接第一个元素拿到phone对象后,直接进行拼接。这样的话,我们就可以助助二次排序的方案来解决这个问题。这里不详细说明二次排序如果有不明白的可以参考《MapReduce-自定义Key-二次排序》《MapReduce-三次排序-曾经想不通的二次排序
那么下面就开始解决问题:
测试数据同上一篇:
用户数据:
uid,name,phoneid
1,tom,40
2,jack,20
3,seven,30
4,lee,10
5,smith,20
6,张三,10
7,李四,30
8,王五,20

goodid,name
10,苹果
20,三星
30,LG
40,华为

输出结果:
lee 苹果
张三 苹果
jack 三星
smith 三星
王五 三星
seven LG
李四 LG
tom 华为

 

 

定义key:

 

 

 

[java] view plain copy

  1. import java.io.DataInput;  
  2. import java.io.DataOutput;  
  3. import java.io.IOException;  
  4. import org.apache.hadoop.io.WritableComparable;  
  5. public class UserKey implements WritableComparable<UserKey>{  
  6.     private String pno = "";  
  7.     private boolean isPhone = false;  
  8.     public UserKey() {  
  9.         super();  
  10.     }  
  11.     public UserKey(String pno, boolean isPhone) {  
  12.         super();  
  13.         this.pno = pno;  
  14.         this.isPhone = isPhone;  
  15.     }  
  16.     public String getPno() {  
  17.         return pno;  
  18.     }  
  19.     public void setPno(String pno) {  
  20.         this.pno = pno;  
  21.     }  
  22.     public boolean isPhone() {  
  23.         return isPhone;  
  24.     }  
  25.     public void setPhone(boolean isPhone) {  
  26.         this.isPhone = isPhone;  
  27.     }  
  28.     @Override  
  29.     public void write(DataOutput out) throws IOException {  
  30.         out.writeUTF(pno);  
  31.         out.writeBoolean(isPhone);;  
  32.     }  
  33.     @Override  
  34.     public void readFields(DataInput in) throws IOException {  
  35.         this.pno = in.readUTF();  
  36.         this.isPhone = in.readBoolean();  
  37.     }  
  38.     /** 
  39.      * 二次排序的比较器保证进入reduce迭代器时phone对象在迭代器的第一个元素处 
  40.      */  
  41.     @Override  
  42.     public int compareTo(UserKey o) {  
  43.         if(!this.pno.equals(o.getPno())) {  
  44.             return this.pno.compareTo(o.getPno());  
  45.         } else {  
  46.             return isPhone ? -11;  
  47.         }  
  48.     }  
  49. }  

定制序列化对象:

 

 

[java] view plain copy

  1. import java.io.DataInput;  
  2. import java.io.DataOutput;  
  3. import java.io.IOException;  
  4. import org.apache.hadoop.io.Writable;  
  5.   
  6. public class User implements Writable {  
  7.     private String uno = "";  
  8.     private String name = "";  
  9.     private String pname = "";  
  10.     private String pno = "";  
  11.     private int flag = 0;  
  12.     public User() {  
  13.     }  
  14.     public User(User u) {  
  15.         super();  
  16.         this.uno = u.uno;  
  17.         this.name = u.name;  
  18.         this.pname = u.pname;  
  19.         this.pno = u.pno;  
  20.         this.flag = u.flag;  
  21.     }  
  22.     public User(String uno, String name, String pname, String pno, int flag) {  
  23.         super();  
  24.         this.uno = uno;  
  25.         this.name = name;  
  26.         this.pname = pname;  
  27.         this.pno = pno;  
  28.         this.flag = flag;  
  29.     }  
  30.     @Override  
  31.     public void readFields(DataInput input) throws IOException {  
  32.         this.uno = input.readUTF();  
  33.         this.name = input.readUTF();  
  34.         this.pname = input.readUTF();  
  35.         this.pno = input.readUTF();  
  36.         this.flag = input.readInt();  
  37.     }  
  38.     @Override  
  39.     public void write(DataOutput output) throws IOException {  
  40.         output.writeUTF(uno);  
  41.         output.writeUTF(name);  
  42.         output.writeUTF(pname);  
  43.         output.writeUTF(pno);  
  44.         output.writeInt(flag);  
  45.     }  
  46.     public String getUno() {  
  47.         return uno;  
  48.     }  
  49.     public void setUno(String uno) {  
  50.         this.uno = uno;  
  51.     }  
  52.     public String getName() {  
  53.         return name;  
  54.     }  
  55.     public void setName(String name) {  
  56.         this.name = name;  
  57.     }  
  58.     public String getPname() {  
  59.         return pname;  
  60.     }  
  61.     public void setPname(String pname) {  
  62.         this.pname = pname;  
  63.     }  
  64.     public String getPno() {  
  65.         return pno;  
  66.     }  
  67.     public void setPno(String pno) {  
  68.         this.pno = pno;  
  69.     }  
  70.     public int getFlag() {  
  71.         return flag;  
  72.     }  
  73.     public void setFlag(int flag) {  
  74.         this.flag = flag;  
  75.     }  
  76.     @Override  
  77.     public String toString() {  
  78.         return name + " " + pname;  
  79.     }  
  80. }  

定制分组比较器:(这里不详细讲解用法及执行过程可以参考《MapReduce-自定义比较器》)

 

[java] view plain copy

  1. import org.apache.hadoop.conf.Configuration;  
  2. import org.apache.hadoop.io.WritableComparable;  
  3. import org.apache.hadoop.io.WritableComparator;  
  4. public class GroupingComparator extends WritableComparator {  
  5.     public GroupingComparator() {  
  6.         super(UserKey.classtrue);  
  7.     }  
  8.     public GroupingComparator(Class<? extends WritableComparable> keyClass,  
  9.             boolean createInstances) {  
  10.         super(keyClass, createInstances);  
  11.     }  
  12.     public GroupingComparator(Class<? extends WritableComparable> keyClass,  
  13.             Configuration conf, boolean createInstances) {  
  14.         super(keyClass, conf, createInstances);  
  15.     }  
  16.     public GroupingComparator(Class<? extends WritableComparable> keyClass) {  
  17.         super(keyClass);  
  18.     }  
  19.     /** 
  20.      * 只使用第一个字段进行分组比较,以保证同一pno的user和phone对象在同一个分组里 
  21.      */  
  22.     @Override  
  23.     public int compare(WritableComparable a, WritableComparable b) {  
  24.         UserKey a1 = (UserKey)a;  
  25.         UserKey b1 = (UserKey)b;  
  26.         return a1.getPno().compareTo(b1.getPno());  
  27.     }  
  28. }  

map阶段:

 

 

[java] view plain copy

  1. import java.io.IOException;  
  2. import org.apache.hadoop.io.LongWritable;  
  3. import org.apache.hadoop.io.Text;  
  4. import org.apache.hadoop.mapreduce.Mapper;  
  5.   
  6. public class JoinMapper extends Mapper<LongWritable, Text, UserKey, User> {  
  7.   
  8.     private User u = new User();  
  9.     private UserKey uk = new UserKey();  
  10.     @Override  
  11.     protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {  
  12.         String line = value.toString();  
  13.         /** 
  14.          * 判断是否为空行 
  15.          */  
  16.         if(line.trim().length() <= 0) {  
  17.             return;  
  18.         }  
  19.         String[] arr = line.split(",");  
  20.         /** 
  21.          * 如果是用户数据则把UserKey中的isPhone字段设为false 
  22.          */  
  23.         if (arr.length == 3) {  
  24.             u.setUno(arr[0]);  
  25.             u.setName(arr[1]);  
  26.             u.setFlag(0);  
  27.             uk.setPno(arr[2]);  
  28.             uk.setPhone(false);  
  29.             context.write(uk, u);  
  30.         } else if (arr.length == 2) {  
  31.             /** 
  32.              * 如果是手机数据则把UserKey中的isPhone字段设为false 
  33.              */  
  34.             u.setPname(arr[1]);  
  35.             u.setPno(arr[0]);  
  36.             u.setFlag(1);  
  37.             uk.setPno(arr[0].trim());  
  38.             uk.setPhone(true);  
  39.             /** 
  40.              * 都把要join的字段作为key,这样就可以让其到reduce函数处理时在同一个 
  41.              * 迭代器中,这样就可以在reduce函数中做join的操作 
  42.              */  
  43.             context.write(uk , u);  
  44.         }  
  45.     }  
  46. }  

reduce阶段:

 

 

[java] view plain copy

  1. import java.io.IOException;  
  2. import org.apache.hadoop.io.NullWritable;  
  3. import org.apache.hadoop.io.Text;  
  4. import org.apache.hadoop.mapreduce.Reducer;  
  5.   
  6. public class JoinReducer extends Reducer<UserKey, User, NullWritable, Text> {  
  7.   
  8.     private Text value = new Text();  
  9.     @Override  
  10.     protected void reduce(UserKey key, Iterable<User> values, Context context)  
  11.             throws IOException, InterruptedException {  
  12.         int num = 0;  
  13.         User phone = null;  
  14.         /** 
  15.          * 直接在第一个元素中取出phone数据,后面的数据直接拼接后输出 
  16.          */  
  17.         for(User e: values) {  
  18.             if(num == 0) {  
  19.                 phone = new User(e);  
  20.                 num ++;  
  21.             } else {  
  22.                 e.setPno(phone.getPno());  
  23.                 e.setPname(phone.getPname());  
  24.                 value.set(e.toString());  
  25.                 context.write(null, value);  
  26.             }  
  27.         }  
  28.     }  
  29. }  

启动函数:

[java] view plain copy

  1. import org.apache.hadoop.conf.Configuration;  
  2. import org.apache.hadoop.fs.FileSystem;  
  3. import org.apache.hadoop.fs.Path;  
  4. import org.apache.hadoop.io.NullWritable;  
  5. import org.apache.hadoop.io.Text;  
  6. import org.apache.hadoop.mapreduce.Job;  
  7. import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;  
  8. import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;  
  9.   
  10. public class JobMain {  
  11.     public static void main(String[] args) throws Exception{  
  12.         Configuration configuration = new Configuration();  
  13.         Job job = new Job(configuration, "join-job");  
  14.         job.setJarByClass(JobMain.class);  
  15.         job.setMapperClass(JoinMapper.class);  
  16.         job.setMapOutputKeyClass(UserKey.class);  
  17.         job.setMapOutputValueClass(User.class);  
  18.         job.setGroupingComparatorClass(GroupingComparator.class);  
  19.         job.setReducerClass(JoinReducer.class);  
  20.         job.setOutputKeyClass(NullWritable.class);  
  21.         job.setOutputValueClass(Text.class);  
  22.         FileInputFormat.addInputPath(job, new Path(args[0]));  
  23.         Path outputDir = new Path(args[1]);  
  24.         FileSystem fs  = FileSystem.get(configuration);  
  25.         if(fs.exists(outputDir)) {  
  26.             fs.delete(outputDir, true);  
  27.         }  
  28.         FileOutputFormat.setOutputPath(job, outputDir);  
  29.         System.exit(job.waitForCompletion(true)?0:1);  
  30.     }  
  31. }  

运行结果:

总结:

这里只是对前一篇博客中的join做了一个简单的优化处理,后面会逐步用一些其它的方法来做一些其它方案的做优化,下一篇《MapReduce-Join中级优化-hadoop自带datajoin的解决方法》会说明基本hadoop自带的jar怎么处理JOIN操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值