MR源码解析和join案例

MR源码解析

  1. new Job(): 读取本地文件, xml配置
  2. job.start(): 启动线程
  3. job的run():线程方法
    • runTasks(): 传入对应的接口,启动map或者reduce
    • MapTask类的run(): 设置map阶段的参数,初始化任务,创建上下文对象
      • 创建读取器LineRecordReader
      • 判断是否压缩 compressFactory
      • 如果没有压缩,使用seek方法
      • mapTask的write(),进行溢写
      • mapper类的init()方法,设置溢写百分比和缓冲区大小
      • collector收集器:进行map阶段数据类型检查和分数数量检查
      • keySerializer: 进行数据的序列化,调用自己写的bean对象
      • kvmeta.put(): 写入环形缓冲区
      • mapPhase结束
    • 数据量达到缓冲区的80%,对索引进行快速排序
    • input.close():关闭输入
    • 关闭输出并同时将缓冲区数据按照分区写入磁盘。
      • 如果开启了combine,进行数据合并
    • mergePart:归并分区
    • combine第二次合并,如果溢写次数小于3就不合并了
    • collector.close():关闭环形缓冲区
  4. reduceTask的run方法
    • submit: 5个reduce并行提交
    • cLeanTask:初始化
    • shuffle类:map的排序,recuce中的归并排序
    • Merger合并器:两次归并排序,先内存归并,后磁盘归并
    • 抓取数据:可以从本地或者网络中抓取
    • sort :归并排序
    • reduce阶段:
      • 创建上下文对象
      • 调用reducer的run方法
      • real.write(): LineRecordWrite写入HDFS

使用MR来进行拷贝去重

  1. 拷贝:values写入上下文时需要迭代遍历
  2. 去重:values写入上下文时不遍历

使用MR来实现join操作


  1. 实现TableBean类,四个属性,空参构造器,get-set方法
    • write():序列化
      • out.writeUTF():该方法有换行,不会连在一起
    • readFields(): 反序列化
  2. 实现mapper类
    • setup()
      • 使用context上下文对象获取InputSplit类
      • 强制类型转换为FileSplit类
      • getPath().getName()获取文件名称
    • map()
      • 切分split
      • 封装
      • context写出
java 复制代码
public class TableMapper extends Mapper<LongWritable, Text, Text,TableBean> {

    private String filename;

    private Text outK;

    private TableBean outV;

    //初始化,每个文件开始一次maptask,并进行一次初始化
    //获取到文件的名称
    @Override
    protected void setup(Mapper<LongWritable, Text, Text, TableBean>.Context context) throws IOException, InterruptedException {
        //拿到切片信息
        FileSplit split = (FileSplit) context.getInputSplit();

        filename = split.getPath().getName();

        outK = new Text();
        outV = new TableBean();
    }

    @Override
    protected void map(LongWritable key, Text value, Mapper<LongWritable, Text, Text, TableBean>.Context context) throws IOException, InterruptedException {
        //1. 获取一行
        String line = value.toString();


        //2.判断是哪个文件的
        if(filename.contains("order")){//处理的是订单表
            String[] split = line.split("\t");

            //封装
            outK.set(split[1]);//pid作为key
            outV.setId(split[0]);
            outV.setPid(split[1]);
            outV.setAmount(Integer.parseInt(split[2]));
            outV.setTableName("order");
            outV.setPname("");
        }else{//处理的是商品表
            String[] split = line.split(" ");
//            System.out.println("=========> " + Arrays.toString(split)+" <=========");
//            System.out.println("=========> " + split[1] +" <=========");
            //封装
            outK.set(split[0]);//pid作为key
            outV.setId("");
            outV.setPid(split[0]);
            outV.setAmount(0);
            outV.setTableName("pd");
            outV.setPname(split[1]);
        }

        //写出
        context.write(outK, outV);
    }
}
  1. 实现reduce类
    • 为了分辨map传递过来的数据是哪个表,给bean对象添加一个表名属性
    • 在mapper类中给对应表的抓取过程中添加标记
    • 在获取到value时不能直接使用等于号进行赋值,values是Iterable集合,比较特殊
    • 属性赋值工具类BeanUtils.copyProperties(dest, src);
java 复制代码
public class TableReducer extends Reducer<Text, TableBean, TableBean, NullWritable> {

    private ArrayList<TableBean> orderBeans;
    private TableBean pdBean;

    @Override
    protected void setup(Reducer<Text, TableBean, TableBean, NullWritable>.Context context) throws IOException, InterruptedException {
        //1.创建集合
        orderBeans = new ArrayList<>();
        pdBean = new TableBean();
    }

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

        orderBeans.clear();//清空集合

        //2.遍历赋值
        for (TableBean value : values) {
            if ("order".equals(value.getTableName())) {
                TableBean temp = new TableBean();
                try {
                    BeanUtils.copyProperties(temp,value);
                } catch (IllegalAccessException e) {
                    throw new RuntimeException(e);
                } catch (InvocationTargetException e) {
                    throw new RuntimeException(e);
                }
                orderBeans.add(temp);
            } else {//商品表
                try {
                    BeanUtils.copyProperties(pdBean, value);
                } catch (IllegalAccessException e) {
                    throw new RuntimeException(e);
                } catch (InvocationTargetException e) {
                    throw new RuntimeException(e);
                }
            }
        }

        //循环遍历orderBeans,赋值pdname
        for (TableBean orderBean : orderBeans) {
            orderBean.setPname(pdBean.getPname());
            context.write(orderBean,NullWritable.get());
        }
    }
}

总结:这种写法,在reduce阶段创建了对象和集合,这些方式都是比较消耗资源的,容易造成数据倾斜问题。

MR在环形缓冲区快排时倒排索引,反向溢写,会导致数据反向输出,类似栈结构的的先进后出。

相关推荐
数研小生1 天前
关键词搜索京东列表API技术对接指南
大数据·数据库·爬虫
星辰_mya1 天前
Elasticsearch之下
大数据·elasticsearch·搜索引擎
菜鸟233号1 天前
力扣647 回文子串 java实现
java·数据结构·leetcode·动态规划
qq_12498707531 天前
基于Java Web的城市花园小区维修管理系统的设计与实现(源码+论文+部署+安装)
java·开发语言·前端·spring boot·spring·毕业设计·计算机毕业设计
2601_948374571 天前
商用电子秤怎么选
大数据·python
h7ml1 天前
查券返利机器人的OCR识别集成:Java Tesseract+OpenCV优化图片验证码的自动解析方案
java·机器人·ocr
野犬寒鸦1 天前
从零起步学习并发编程 || 第五章:悲观锁与乐观锁的思想与实现及实战应用与问题
java·服务器·数据库·学习·语言模型
Volunteer Technology1 天前
Sentinel的限流算法
java·python·算法
岁岁种桃花儿1 天前
SpringCloud从入门到上天:Nacos做微服务注册中心
java·spring cloud·微服务
jdyzzy1 天前
什么是 JIT 精益生产模式?它与传统的生产管控方式有何不同?
java·大数据·人工智能·jit