13.mapReduce的join思想和实现方法

  一、在mapReduce的实现过程中,我们可能遇到这样的需求:

    现在有两类数据,存放在同一个文件夹下,一类是商品的订单数据,一类是商品的信息数据。
    

    商品的订单数据格式如下:
    订单号 商品号 商品单价
    10001 x01 99.00
    ......
    

    商品的信息数据格式如下:
      商品号 商品名称
      x01 iphone6plus
    

    而现在需要
      订单号 商品名称 商品单价
    这样的数据格式数据,如何实现?

  二、reducejoin

   

    在map阶段,我们将商品号作为key,订单号,商品单价,商品名称,在来一个文件来源属性,文件名称来map出这样的数据:
      key value
      商品号 订单号、商品单价、商品名称、文件名称
    在reduce阶段,我们将map根据key相同组合的数据拿出, 遍历迭代器:
      1.判断如果是来自b.txt的话,去除本次key对应的商品名称。
      2.如果不是,则新建一个bean,填充数据,存放到list集合。
    有了集合之后,我们可以将list里面的bean对象的商品名称赋值,然后context。write()即可。

    

    拿到文件名的api: 

      FileSplit inputSplit = (FileSplit) context.getInputSplit();
      String fileName = inputSplit.getPath().getName();

    代码:

    

package com.xws.join;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.NullWritable;
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.input.FileSplit;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
//com.xws.reduceJoin.ReduceJoin
public class ReduceJoin {
    
    public static class ReduceJoinMapper extends Mapper<LongWritable, Text, Text, JoinBean>{

        @Override
        protected void map(LongWritable key, Text value, Context context)
                throws IOException, InterruptedException {
            
            //获得每行数据
            String [] fields = value.toString().split("\t");
            
            //获得本行数据的来源的文件名称
            FileSplit inputSplit = (FileSplit) context.getInputSplit();
            String fileName = inputSplit.getPath().getName();
            
            //定义bean中的数据变量,分a.txt和b.txt分别赋值
            String orderid="";
            String spid="";
            float price=0;
            String spname="";
            
            if("a.txt".equals(fileName)){
                orderid = fields[0];
                spid = fields[1];
                price = Float.parseFloat(fields[2]);
            }else{
                spid = fields[0];
                spname = fields[1];
            }
             context.write(new Text(spid), new JoinBean(orderid, spid, price, spname, fileName));
        }
    }
    
    public static class ReduceJoinReducer extends Reducer<Text, JoinBean, JoinBean, NullWritable>{

        @Override
        protected void reduce(Text key, Iterable<JoinBean> values,
                Context context)
                throws IOException, InterruptedException {
            //定义一个spname变量来接收该key对应的b.txt文件中的商品名称信息
            String spname ="";
            //定义一个list来接收来自a文件的数据
            List<JoinBean> list = new ArrayList<JoinBean>();
            for (JoinBean joinBean : values) {
                if(joinBean.getFileName().equals("b.txt")){
                    spname=joinBean.getSpname();
                }else{
                    //如果不是b文件中的数据,那么a文件中的商品id、商品单价、以及我们已经获取到的本key对应的商品名称来构建一个新的JoinBean
                    //为什么不能用迭代器中的joinBean,因为迭代器中的joinBean每次都是同一个对象,只是在迭代的时候对它的每隔变量赋了新值
                    //如果在这里直接add到list中时,那么都是同一个对象,即迭代的第一个对象
                    JoinBean jb = new JoinBean();
                    jb.setOrderid(joinBean.getOrderid());
                    jb.setPrice(joinBean.getPrice());
                    list.add(jb);
                }
            }
            //对每一个a文件的数据赋商品名称的值
            for (JoinBean joinBean : list) {
                joinBean.setSpname(spname);
                context.write(joinBean, NullWritable.get());
            }
        }
    }
    
    public static void main(String[] args) throws Exception {
        Configuration conf = new Configuration();
        Job job = Job.getInstance(conf);
        job.setJarByClass(ReduceJoin.class);
        
        job.setMapperClass(ReduceJoinMapper.class);
        job.setReducerClass(ReduceJoinReducer.class);
        
        job.setOutputKeyClass(ReduceJoin.class);
        job.setOutputValueClass(NullWritable.class);
        
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(JoinBean.class);
        
        FileInputFormat.setInputPaths(job, new Path(args[0]));
        FileOutputFormat.setOutputPath(job, new Path(args[1]));
        
        job.waitForCompletion(true);
        
        
    }
    
}

 

package com.xws.join;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;

import org.apache.hadoop.io.Writable;

public class JoinBean implements Writable{

    private String orderid;
    private String spid;
    private float price;
    private String spname;
    private String fileName;
    
    
    public JoinBean() {    }
    
    public JoinBean(String orderid, String spid, float price,String spname, String fileName) {
        super();
        this.orderid = orderid;
        this.spid = spid;
        this.price = price;
        this.spname = spname;
        this.fileName = fileName;
    }
    public String getOrderid() {
        return orderid;
    }
    public void setOrderid(String orderid) {
        this.orderid = orderid;
    }
    public String getSpid() {
        return spid;
    }
    public void setSpid(String spid) {
        this.spid = spid;
    }
    
    public float getPrice() {
        return price;
    }

    public void setPrice(float price) {
        this.price = price;
    }
    public String getSpname() {
        return spname;
    }

    public void setSpname(String spname) {
        this.spname = spname;
    }
    public String getFileName() {
        return fileName;
    }
    public void setFileName(String fileName) {
        this.fileName = fileName;
    }

    @Override
    public void readFields(DataInput in) throws IOException {
        this.orderid = in.readUTF();
        this.spid = in.readUTF();
        this.price = in.readFloat();
        this.spname = in.readUTF();
        this.fileName = in.readUTF();
    }

    @Override
    public void write(DataOutput out) throws IOException {
        out.writeUTF(orderid);
        out.writeUTF(spid);
        out.writeFloat(price);
        out.writeUTF(spname);
        out.writeUTF(fileName);
        
    }

    @Override
    public String toString() {
        return orderid+"\t"+spname+"\t"+"price";
    }
    
    
    
}

  三、mapjoin 

      reduce的join逻辑思想容易出现数据倾斜的的问题,如果某一类商品的数据过多,出现其他reducetask都处理完了,
    它仍然在运行的情况,拖慢了整体的效率。 

      解决方案:
      在map阶段就join,在map阶段,我们可以重写set方法,将数据保留在内存中,map阶段取出去替换我们需要的数据。
    这样在reduce阶段,直接输出数据就行。(不写reduce也可以)

      代码:

package com.xws.join;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.io.IOUtils;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
//com.xws.join.MapJoin
public class MapJoin {

    public static class mapJoin extends Mapper<LongWritable, Text, Text, NullWritable>{
        
        Map<String,String> spMap = null;
        
        /**
         *在maptask之前,进行数据缓存记录
         */
        @Override
        protected void setup(Context context) throws IOException,
                InterruptedException {
            spMap = new HashMap<String,String>();
            FileReader fileReader = new FileReader("b.txt");
            BufferedReader br = new BufferedReader(fileReader);
            
            String line =null;
            while((line=br.readLine())!=null){
                String [] fields =line.split("\t");
                spMap.put(fields[0], fields[1]);
            }
            IOUtils.closeQuietly(br);
            IOUtils.closeQuietly(fileReader);
        }
        
        @Override
        protected void map(LongWritable key, Text value, Context context)
                throws IOException, InterruptedException {
            
            String line =value.toString();
            String [] fields = line.split("\t");
            
            fields[1]=spMap.get(fields[1]);
            
            context.write(new Text(fields[0]+"\t"+fields[1]+"\t"+fields[2]), NullWritable.get());
        }

    }
    
    public static void main(String[] args) throws IOException, Exception {
        Configuration conf = new Configuration();
        Job job = Job.getInstance(conf);
        
        job.setJarByClass(MapJoin.class);
        
        job.setMapperClass(mapJoin.class);
        
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(NullWritable.class);
        
        FileInputFormat.setInputPaths(job, new Path(args[0]));
        FileOutputFormat.setOutputPath(job, new Path(args[1]));
        

        //将小表加入分布式缓存中,hadoop会自动帮我们把这个小表分发给每一个map task进程,放在它们的本地工作目录中
        job.addCacheFile(new URI("hdfs://hadoop1:9000/mapjoin/cachefile/b.txt"));
        
        job.setNumReduceTasks(0);
        job.waitForCompletion(true);
    }
}

 

posted @ 2016-08-15 11:03  博智星  Views(195)  Comments(0)    收藏  举报