大数据知识专栏 - MapReduce 的 Map端Join

接上一篇Reduce端Join, 当进行表文件join的时候, 如果其中有一个是小表(文件小), 可以将小表加载到分布式缓存中, 通过Map端join来减少shuffle过程网络数据传输, 达到shuffle调优的目的

Map端Join

概述

Reduce端Join: 需要数据在map端经过shuffle发送到reduce端, 增加网络传输, 如果可以不发送到reduce端, 而在map端即进行join, 可以提高效率.

前提:有小表, 小表放分布式缓存

实现原理

​ 适用于关联表中有小表的情形. 使用分布式缓存, 可以将小表分发到所有的map节点,这样,map节点就可以在本地对自己所读到的大表数据进行join并输出最终结果,可以大大提高join操作的并发度,加快处理速度.

应用举例

需求: 使用MapReduce输出上面SQL的结果, 给订单加上商品名称, 商品分类, 商品价格 (id,date,pname,category_id,price)

样例数据:

product.txt

P0001	华为Mate40Pro	c00001	7999
P0002	苹果Iphone12	c00001	7999
P0003	小米10	c00001	4999

order.txt

100001	20201231	P0001	1
100002	20201231	P0002	1
100003	20201231	P0003	2

1, Mapper类

import org.apache.hadoop.fs.FSDataInputStream;
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.Mapper;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URI;
import java.util.HashMap;

public class MapperJoinMapper extends Mapper<LongWritable, Text, NullWritable, Text> {
    private HashMap<String, String> productMap = new HashMap<String, String>();

    // 读取分布式缓存中的小表数据
    @Override
    protected void setup(Context context) throws IOException, InterruptedException {
        //1, 获取分布式缓存文件列表
        URI[] cacheFiles = context.getCacheFiles();
        //2, 获取小表所在的文件系统
        URI cacheFile = cacheFiles[0];
        FileSystem fileSystem = FileSystem.get(cacheFile, context.getConfiguration());
        //3, 获取指向小表文件的输入流
        FSDataInputStream inputStream = fileSystem.open(new Path(cacheFile));
        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
        //4, 读取文件的每一行内容, 保存数据到全局变量Map集合<id,行内容>
        String line = null;
        while ((line = reader.readLine()) != null) {
            String[] product = line.split("\t");
            productMap.put(product[0], line);
        }
        //5, 关闭资源
        reader.close();
        fileSystem.close();
    }

    // 读取大表数据, 并与缓存中读取到的小表数据做join操作
    @Override
    protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
        //1,在代表大表Order的行文本中获取到商品id, 作为key
        String[] fields = value.toString().split("\t");
        String productID = fields[2];
        //2, 在小表Map集合中获取到商品id对应的值, 作为value
        String productInfor = productMap.get(productID);
        //3, 将key,value写入到上下文中
        context.write(NullWritable.get(), new Text(productInfor + "\t" + value.toString()));
    }
}

2, Job启动类

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.TextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;
import java.net.URI;

public class MainJob extends Configured implements Tool {

    @Override
    public int run(String[] args) throws Exception {
        //获取Job对象
        Job job = Job.getInstance(super.getConf(), "MappJoin");

        // *关键: * 将小表放入到分布式缓存中
        job.addCacheFile(new URI("hdfs://hdfs小表文件地址"));

        //1,设置输入类和文件输入路径
        job.setInputFormatClass(TextInputFormat.class);
        TextInputFormat.addInputPath(job,new Path("输入文件路径"));

        //2, 设置Mapper类和数据类型
        job.setMapperClass(MapperJoinMapper.class);
        job.setMapOutputKeyClass(NullWritable.class);
        job.setMapOutputValueClass(Text.class);

        //3, 设置输出类和文件输出路径
        job.setOutputFormatClass(TextOutputFormat.class);
        TextOutputFormat.setOutputPath(job,new Path("输出文件路径"));

        //启动任务
        boolean completion = job.waitForCompletion(true);
        return completion?1:0;
    }

    public static void main(String[] args) {
        Configuration configuration = new Configuration();
        int runResult = 0;
        try {
            runResult = ToolRunner.run(configuration, new MainJob(), args);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.exit(runResult);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

能力工场小马哥

如果对您有帮助, 请打赏支持~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值