MapReduce 实现 join 文件数据(四)

我们都知道,当对两个表进行关联的时候可以用sql的join语句简单的去实现,并且如果两张表的数据查询非常大,那么一般会讲小表放在左边,可以达到优化的作用,为何呢?其实我们在使用mapreduce的时候小表可以先加载到内存中,然后再与输入数据进行对比,如果匹配成功则关联输出。今天我们将介绍使用mapreduce中mapjoin与reducejoin两种方式对数据的关联并输出。
一、先看数据:
image.png
我们分别将两个数据文件放到hdfs上:
image.png
二、以 order 作为小表在 map 中进行 join,首先我们创建驱动类框架:
public class MapJoinRM extends Configured implements Tool {

    //加载到内存中的对象
    static Map<String, String> customerMap = new HashMap<String, String>();

    public int run(String[] args) throws Exception {

        //driver
        //1) 获取配置对象
        Configuration configuration = this.getConf();

        //2) 创建任务对象
        Job job = Job.getInstance(configuration, this.getClass().getSimpleName());
        job.setJarByClass(this.getClass());

        //3.1) 设置输入
        Path path = new Path(args[0]);
        FileInputFormat.addInputPath(job, path);

        //3.2) map 的设置
        job.setMapperClass(JoinMapper.class);
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(Text.class);

        //3.3 reduce 设置

        //3.4 添加缓存
        URI uri = new URI(args[2]);
        job.addCacheFile(uri);

        //3.5 设置输出
        Path output = new Path(args[1]);
        FileOutputFormat.setOutputPath(job, output);

        //4. 提交
        boolean sucess = job.waitForCompletion(true);
        return sucess ? 0 : 1;
    }

    public static void main(String[] args) {

        args = new String[]{
                "hdfs://bigdata-pro01.lcy.com:9000/user/hdfs/order.txt",
                "hdfs://bigdata-pro01.lcy.com:9000/user/hdfs/output66",
                "hdfs://bigdata-pro01.lcy.com:9000/user/hdfs/customer.txt"
        };

        Configuration configuration = new Configuration();
        try {
            //判断是否已经存在路径
            Path fileOutputPath = new Path(args[1]);
            FileSystem fileSystem = FileSystem.get(configuration);
            if(fileSystem.exists(fileOutputPath)){
                fileSystem.delete(fileOutputPath, true);
            }

            int status = ToolRunner.run(configuration, new MapJoinRM(), args);
            System.exit(status);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

}

三、实现 mapper 子类处理缓存数据以及关联逻辑的实现:
public static class JoinMapper extends Mapper<LongWritable, Text, Text, Text>{

        private Text outputKey = new Text();
        private Text outputValue = new Text();
  
        @Override
        protected void setup(Context context) throws IOException, InterruptedException {
            //缓存数据的处理
            Configuration configuration = context.getConfiguration();
            URI[] uri = Job.getInstance(configuration).getCacheFiles();
            Path path = new Path(uri[0]);
            FileSystem fileSystem = FileSystem.get(configuration);
            InputStream inputStream = fileSystem.open(path);

            InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);

            String line = null;
            while((line = bufferedReader.readLine()) != null){
                if(line.trim().length() > 0){
                    customerMap.put(line.split(",")[0], line);
                }
            }

            bufferedReader.close();
            inputStreamReader.close();
            inputStream.close();
        }

        @Override
        protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {

            String lineValue = value.toString();
            StringTokenizer stringTokenizer = new StringTokenizer(lineValue, ",");
            while(stringTokenizer.hasMoreTokens()){
                String wordValue = stringTokenizer.nextToken();
                if(customerMap.get(wordValue) != null){
                    outputKey.set(wordValue);
                    outputValue.set(customerMap.get(wordValue) + lineValue);
                    context.write(outputKey, outputValue);
                    break;
                }
            }

        }

        @Override
        protected void cleanup(Context context) throws IOException, InterruptedException {

        }
    }
四、运行程序并在控制台中命令查看关联结果:
bin/hdfs dfs -text /user/hdfs/output66/part*

运行结果如图:


image.png

大小表的关联就这么简单,接下来我们使用 reduce 的进行 join

五、由于在 reduce 中进行 join 的话是同时加载两个数据进来的,为了区分从 map 中传进来的数据,我们要自定义一个类型,设置一个变量用于标识是哪张表的数据,这样我们在reduce中才能区分哪些数据是属于哪张表的:
public class DataJoionWritable implements Writable {

    private String tag;
    private String data;

    public DataJoionWritable() {
    }

    public DataJoionWritable(String tag, String data) {
       this.set(tag, data);
    }

    public void set(String tag, String data){
        this.tag = tag;
        this.data = data;
    }

    public void write(DataOutput dataOutput) throws IOException {

        dataOutput.writeUTF(this.getTag());
        dataOutput.writeUTF(this.getData());

    }

    public void readFields(DataInput dataInput) throws IOException {

        this.setTag(dataInput.readUTF());
        this.setData(dataInput.readUTF());

    }

    public String getTag() {
        return tag;
    }

    public void setTag(String tag) {
        this.tag = tag;
    }

    public String getData() {
        return data;
    }

    public void setData(String data) {
        this.data = data;
    }

    @Override
    public String toString() {
        return "DataJoionWritable{" +
                "tag='" + tag + '\'' +
                ", data='" + data + '\'' +
                '}';
    }

}
六、为了方便使用表示常量我们创建一个常用类:
public class DataCommon {

    public final static String CUSTOMER = "customer";
    public final static String ORDER = "order";

}
七、创建驱动类的通用框架:
public class ReduceJoinMR extends Configured implements Tool {

    public int run(String args[]) throws IOException, ClassNotFoundException, InterruptedException {

        //driver
        //1) 获取配置对象
        Configuration configuration = this.getConf();

        //2) 创建任务对象
        Job job = Job.getInstance(configuration, this.getClass().getSimpleName());
        job.setJarByClass(this.getClass());

        //3.1) 设置输入
        Path path = new Path(args[0]);
        FileInputFormat.addInputPath(job, path);

        //3.2) map 的设置
        job.setMapperClass(JoinMapper2.class);
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(DataJoionWritable.class);

        //3.3 reduce 设置
        job.setReducerClass(JoinReduce2.class);
        job.setOutputKeyClass(NullWritable.class);
        job.setOutputValueClass(Text.class);

        //3.4 设置输出
        Path output = new Path(args[1]);
        FileOutputFormat.setOutputPath(job, output);

        //4. 提交
        boolean sucess = job.waitForCompletion(true);
        return sucess ? 0 : 1;
    }


    public static void main(String[] args) {
        //datas目录下有已存在要关联的两个数据文件
        args = new String[]{
                "hdfs://bigdata-pro01.lcy.com:9000/user/hdfs/datas",
                "hdfs://bigdata-pro01.lcy.com:9000/user/hdfs/output100"
        };

        Configuration configuration = new Configuration();
        try {
            //判断是否已经存在路径
            Path fileOutputPath = new Path(args[1]);
            FileSystem fileSystem = FileSystem.get(configuration);
            if(fileSystem.exists(fileOutputPath)){
                fileSystem.delete(fileOutputPath, true);
            }

            int status = ToolRunner.run(configuration, new ReduceJoinMR(), args);
            System.exit(status);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

}

八、接下来我们开始实现 Mapper 的数据逻辑的处理:
public static class JoinMapper2 extends Mapper<LongWritable, Text, Text, DataJoionWritable>{

        private Text outputKey = new Text();
        DataJoionWritable outputValue = new DataJoionWritable();

        @Override
        protected void setup(Context context) throws IOException, InterruptedException {

        }

        @Override
        protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {

            String[] values = value.toString().split(",");
            if((3 != values.length) && (4 != values.length)) return;

            //customer
            if(3 == values.length){
                String cid = values[0];
                String name = values[1];
                String telphone = values[2];
                outputKey.set(cid);
                outputValue.set(DataCommon.CUSTOMER,name + ","+telphone);
            }

            //order
            if(4 == values.length){
                String cid = values[1];
                String price = values[2];
                String productName = values[3];
                outputKey.set(cid);
                outputValue.set(DataCommon.ORDER,productName + ","+price);
            }

            context.write(outputKey,outputValue);

        }

        @Override
        protected void cleanup(Context context) throws IOException, InterruptedException {

        }
    }

九、使用 reduce 对数据的关联处理:
public static class JoinReduce2 extends Reducer<Text, DataJoionWritable, NullWritable, Text>{

        private  Text outputValue = new Text();

        @Override
        protected void setup(Context context) throws IOException, InterruptedException {

        }

        @Override
        protected void reduce(Text key, Iterable<DataJoionWritable> values, Context context) throws IOException, InterruptedException {

            String customerInfo = null;
            List<String> orderList = new ArrayList<String>();

            for (DataJoionWritable dataJoinWritable : values){
                if(DataCommon.CUSTOMER.equals(dataJoinWritable.getTag())){
                    customerInfo = dataJoinWritable.getData();
                }
                else if(DataCommon.ORDER.equals(dataJoinWritable.getTag())){
                    orderList.add(dataJoinWritable.getData());
                }
            }

            for (String orderInfo : orderList){
                if(customerInfo == null) continue;
                outputValue.set(key.toString() +","+ customerInfo + ","+ orderInfo);
                context.write(NullWritable.get(),outputValue);
            }

        }

        @Override
        protected void cleanup(Context context) throws IOException, InterruptedException {

        }
    }

十、使用命令查询结果如下:
image.png

由于时间过于紧迫,基本上就粘贴代码了,后续会优化,在此感谢老师的思路。。。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容

  • 关于Mongodb的全面总结 MongoDB的内部构造《MongoDB The Definitive Guide》...
    中v中阅读 31,789评论 2 89
  • 一.简述如何安装配置apache 的一个开源的hadoop 1.使用root账户登陆 2.修改ip 3.修改hos...
    栀子花_ef39阅读 4,888评论 0 52
  • 我只是写出我的所想而已,给自己多年的自己观看,让自己知道原来某天某时某刻我是如此的想法。 明天是周六了。既期待又害...
    此时此刻521阅读 422评论 0 1
  • 我有一万个去见你的理由,唯独没有一个去见你的身份。愿我们各自安好。若上天再给我一次选择的机会,我会选择从没遇见你,...
    白系阅读 104评论 0 0
  • 这周从周一起每天早出晚归,期中考、论文、五四表彰大会,所以有了很极端的想法:放假要窝三天,吃了睡睡了吃。结果...
    孤单增幅装置阅读 436评论 0 0