MapReduce实现CommonJoin和MapJoin

##CommnoJoin和MapJoin简介

CommonJoin即传统思路实现Join,性能较差 因为涉及到了shuffle的过程
common join/shuffle join/reduce join (都是指同一个)
MapJoin 也叫作 boardcast join,但是MapJoin不会有reduce阶段和shuffle阶段

本篇文章,将对CommonJoin和MapJoin,通过MapReduce进行实现
将有助于理解后续的Hive中的Join

##CommnJoin的MapReduce实现

CommonJoinMap

package com.zhaotao.bigdata.commonjoin;

import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.lib.input.FileSplit;

import java.io.IOException;

/**
 * Created by 陶 on 2017/10/9.
 */
public class CommonJoinMap extends Mapper{

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

        // 获取输入文件的全路径和名称
        FileSplit fileSplit = (FileSplit) context.getInputSplit();
        String path = fileSplit.getPath().toString();

        // 获取输入记录的字符串
        String line = value.toString();
        
        // 处理来自emp表的记录
        if (path.contains("emp")){
            // 按空格切割
            String[] values = line.split(" ");
            // 获取emp表的部门编号和员工名字
            String deptNo = values[7];
            String empName = values[1];

            // 把结果写出去,打标签
            context.write(new Text(deptNo), new Text("a#" + empName));
        } else if (path.contains("dept")){
            // 按空格符切割
            String[] values = line.split(" ");
            // 获取dept表的部门编号、部门名称、城市
            String deptNo = values[0];
            String deptName = values[1];
            String city = values[2];

            // 把结果写出去,打标签
            context.write(new Text(deptNo), new Text("b#" + deptName + " " + city));
        }
    }
    
}

CommonJoinReduce

package com.zhaotao.bigdata.commonjoin;

import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;

import java.io.IOException;
import java.util.Vector;

/**
 * Created by 陶 on 2017/10/9.
 */
public class CommonJoinReduce extends Reducer{

    @Override
    protected void reduce(Text key, Iterable values, Reducer.Context context)
            throws IOException, InterruptedException {

        // 用于存放来自emp表的数据
        Vector vectorA = new Vector();
        // 用于存放来自dept表的数据
        Vector vectorB = new Vector();

        // 迭代集合数据
        for (Text val : values){
            // 将集合中的数据 对应 添加到  相应的  Vector中去
            // 根据标签 ,将 a# 和 b# 之后的数据全部提取出来
            // 相当于去除标签
            if (val.toString().startsWith("a#")){
                vectorA.add(val.toString().substring(2));
            } else if (val.toString().startsWith("b#")){
                vectorB.add(val.toString().substring(2));
            }
        }

        // 获取两个Vector集合的长度
        int sizeA = vectorA.size();
        int sizeB = vectorB.size();

        // 遍历两个向量,将结果写出去
        for (int i = 0 ; i < sizeA ; i ++){
            for (int j = 0 ; j < sizeB ; j ++){
                context.write(key, new Text(" " + vectorA.get(i) + " " + vectorB.get(j)));
            }
        }
    }

}

CommonJoinDriver

package com.zhaotao.bigdata.commonjoin;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.input.TextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;

import java.net.URI;

/**
 * Created by 陶 on 2017/10/9.
 */
public class CommonJoinDriver {

    public static void main(String[] args) throws Exception{

        // 定义输入路径
        String INPUT_PATH = "/input/*";
        // 定义输出路径
        String OUT_PATH = "/out/commonjoin_out";

        // 创建配置信息
        Configuration conf = new Configuration();

        // 创建文件系统
        FileSystem fileSystem = FileSystem.get(new URI(OUT_PATH), conf);
        // 如果输出目录存在,我们就删除
        if (fileSystem.exists(new Path(OUT_PATH))) {
            fileSystem.delete(new Path(OUT_PATH), true);
        }

        // 创建任务
        Job job = new Job(conf, CommonJoinDriver.class.getName());

        // 打成jar包运行,这句话是关键
        job.setJarByClass(CommonJoinDriver.class);

        // 设置输入目录和设置输入数据格式化的类
        FileInputFormat.setInputPaths(job, INPUT_PATH);
        job.setInputFormatClass(TextInputFormat.class);

        // 设置自定义Mapper类和设置map函数输出数据的key和value的类型
        job.setMapperClass(CommonJoinMap.class);
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(Text.class);

        // 指定Reducer类和输出key和value的类型
        job.setReducerClass(CommonJoinReduce.class);
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(Text.class);

        // 指定输出的路径和设置输出的格式化类
        FileOutputFormat.setOutputPath(job, new Path(OUT_PATH));
        job.setOutputFormatClass(TextOutputFormat.class);


        // 提交作业 退出
        System.exit(job.waitForCompletion(true) ? 0 : 1);
    }

}

##CommonJoin结果
MapReduce实现CommonJoin和MapJoin_第1张图片

##MapJoin的MapReduce实现

MapJoinMapper

package com.zhaotao.bigdata.mapjoin;

import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.filecache.DistributedCache;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by 陶 on 2017/10/10.
 */
public class MapJoinMapper extends Mapper{

    private Map joinData = new HashMap();

    /**
     * 读取小表数据
     * 使用DistributedCache,将数据缓存到分布式内存中来
     * @param context
     * @throws IOException
     * @throws InterruptedException
     */
    @Override
    protected void setup(Context context) throws IOException, InterruptedException {
        // 预处理把要关联的文件加载到缓存中来
        Path[] paths = DistributedCache.getLocalCacheFiles(context.getConfiguration());
        System.out.println(paths[0]);
        // 创建BufferReader去读取小表
        BufferedReader reader = new BufferedReader(new FileReader(paths[0].toString()));

        String str = null;
        try {
            // 一行一行读取
            while ((str = reader.readLine()) != null){
                // 对缓存中小表(dept表)的数据进行切割
                String[] splits = str.split(" ");
                // 把字符数组中有用的数据存在一个Map中
                // 部门编号  部门名称
                joinData.put(splits[0],splits[1]);
            }
        } catch (Exception e){
            e.printStackTrace();
        } finally {
            reader.close();
        }
    }

    @Override
    protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
        // 获取从HDFS中加载的大表(emp表)
        String[] values = value.toString().split(" ");
        String empName = values[1];
        // 获取关联的字段deptNo,这个字段是关键
        String deptNo = values[7];
        // 根据从内存中的关联表中获取要关联的属性deptName
        String deptName = joinData.get(deptNo);

        // 写出去
        context.write(new Text(deptNo), new Text(empName + " " + deptName));
    }
}

MapJoinDriver

package com.zhaotao.bigdata.mapjoin;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.filecache.DistributedCache;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.input.TextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;

import java.net.URI;

/**
 * 因为涉及到分布式缓存,只能放在集群环境下跑
 *
 * Created by 陶 on 2017/10/10.
 */
public class MapJoinDriver {

    public static void main(String[] args) throws Exception {

        // 定义输入路径
        String INPUT_PATH1 = "/input/emp.txt";
        // 加载到内存的表的路径
        String INPUT_PATH2 = "/input/dept.txt";
        // 定义输出路径
        String OUT_PATH = "/out/mapjoin_out";

        // 创建配置信息
        Configuration conf = new Configuration();

        // 创建文件系统
        FileSystem fileSystem = FileSystem.get(new URI(OUT_PATH), conf);

        // 如果输出目录存在,我们就删除
        if (fileSystem.exists(new Path(OUT_PATH))) {
            fileSystem.delete(new Path(OUT_PATH), true);
        }

        // 添加到内存中的文件(随便添加多少个文件)
        DistributedCache.addCacheFile(new Path(INPUT_PATH2).toUri(), conf);

        // 创建任务
        Job job = new Job(conf, MapJoinDriver.class.getName());

        // 打成jar包运行,这句话是关键
        job.setJarByClass(MapJoinDriver.class);

        // 设置输入目录和设置输入数据格式化的类
        FileInputFormat.setInputPaths(job, INPUT_PATH1);
        job.setInputFormatClass(TextInputFormat.class);

        // 设置自定义Mapper类和设置map函数输出数据的key和value的类型
        job.setMapperClass(MapJoinMapper.class);
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(Text.class);

        job.setNumReduceTasks(0);

        FileOutputFormat.setOutputPath(job, new Path(OUT_PATH));
        job.setOutputFormatClass(TextOutputFormat.class);
        // 提交作业 退出
        System.exit(job.waitForCompletion(true) ? 0 : 1);

    }

}

##MapJoin运行结果

运行结果:
MapReduce实现CommonJoin和MapJoin_第2张图片

验证没有reduce,没有shuffle过程:
MapReduce实现CommonJoin和MapJoin_第3张图片

你可能感兴趣的:(Hadoop)