mapreduce学习工程之五---map端join连接

实验环境 win7 hadoop2.7.3本地模式

实验数据:订单数据orders.txt,商品数据pdts.txt

order.txt

1001    pd001    300
1002    pd002    20
1003    pd003    40
1004    pd002    50

pdts.txt

pd001    apple
pd002    xiaomi
pd003    cuizi

实验解决的问题:解决mapreduce连接过程中的数据倾斜的问题,典型应用场景如下:在电商平台中,买小米手机和买苹果手机的订单数量很多,买锤子手机的订单数量很少,如

果根据传统的mapreduce方法,3个reduce的数据将不均衡。比如接受小米的reduce接收到的数据会很多,接受锤子数据的reduce接收到的数据就会很少

实验解决的思路:采用map端连接,直接将排序过程在map中执行,将商品信息加载在map信息中,引入mapreduce的输入缓存机制

代码如图所示:

package com.tianjie.mapsidejoin;

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

import org.apache.commons.lang.StringUtils;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
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;

public class MapSideJoin {

    static class MapSideJoinMappe extends Mapper<LongWritable, Text, Text, NullWritable>{
        
        
        //map 商品的订单信息k v key为商品编号,v为商品名称
        Map<String,String>     pdInfoMap = new HashMap<String, String>();
        Text ktext = new Text();
        
        
        /*setup 函数用来加载文件到hadoop缓存中
         * */
        protected void setup(Mapper<LongWritable, Text, Text, NullWritable>.Context context)
                throws IOException, InterruptedException {
            
            //打开输入文本文件的路径,获得一个输入流
            BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("C:/Users/admin/Desktop/join/cache/pdts.txt")));
            String line;
            while(StringUtils.isNotEmpty(line = br.readLine())){
                //获得商品信息表 k为商品编号,value为商品名称
                String[] split = line.split("\t");
                pdInfoMap.put(split[0], split[1]);
                
            }
            
        }
        /*
         * hadoop 的缓冲机制*/
        
        
        /*
         * map 函数的输入key value ,其中默认输入为TextInputFormat,
         *     key 为输入文本的偏移量,value为输入文本的值
         *     Text,NullWriable为map文件输入的值
         *     */
        protected void map(LongWritable key, Text value, Mapper<LongWritable, Text, Text, NullWritable>.Context context)
                throws IOException, InterruptedException {
            
            //获得文本文件的一行
            String orderline  = value.toString();
            //将文本文件按照制表符切分
            String[] fields = orderline.split("\t");
            //更具商品编号,获得商品名称
            String pdName = pdInfoMap.get(fields[1]);
            //获得商品的名字,将商品名称追加在文本文件中
            ktext.set(orderline+"\t"+pdName);
            //将新的文本文件写出
            context.write(ktext, NullWritable.get());
        }
        
    }
        
    
    public static void main(String[] args) throws IOException, URISyntaxException, ClassNotFoundException, InterruptedException {
        
        //得到hadoop的一个配置参数
        Configuration conf = new Configuration();
        //获取一个job实例
        Job job = Job.getInstance(conf);
        //加载job的运行类
        job.setJarByClass(MapSideJoin.class);
        
        //加载mapper的类
        job.setMapperClass(MapSideJoinMappe.class);
        //设置mapper类的输出类型
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(NullWritable.class);
        
        //设置文件输入的路径
        FileInputFormat.setInputPaths(job, new Path(args[0]));
        
        //设置文件的输出路径
        FileSystem fs = FileSystem.get(conf);
        Path path = new Path(args[1]);
        if(fs.isDirectory(path)){
            fs.delete(path, true);
        }
        FileOutputFormat.setOutputPath(job, new Path(args[1]));
        
        //指定需要缓冲一个文件到所有maptask运行节点工作目录
        //job.addArchiveToClassPath(""); 缓存jar包到task运行节点的classpath中
        //job.addFileToClassPath(file); 缓存普通文件到task运行节点的classpath中
        //job.addCacheArchive(uri);      缓存压缩包文件到task运行节点的工作目录中
        
        //1:缓存普通文件到task运行节点的工作目录
        job.addCacheFile(new URI("file:///C:/Users/admin/Desktop/join/cache/pdts.txt")); 
        
        //2:指定map端的加入逻辑不需要reduce阶段,设置reducetask数量为0
        job.setNumReduceTasks(0);
        
        //提交job任务,等待job任务的结束
        boolean res =job.waitForCompletion(true);
        System.exit(res?1:0);
        
        }
}

 

需要注意的点有:

1:采用map端连接时,可以不适用reduce,这个时候可以设置reducetask 的数量为0:

2:程序运行的结果:

 

posted @ 2017-03-24 13:29  跨七海的风  阅读(3060)  评论(1编辑  收藏  举报