【Hadoop】MapReduce —— 大数据的核心思想体现


资料来源:拉勾大数据训练营【第二期】

概述

MapReduce 的思想核心分而治之,适用于大量复杂的任务处理场景(大规模数据处理场景)。
最主要的特点就是把一个大的问题划分成很多小的子问题并且每个小的子问题的求取思路与我们大问题的求取思路一样
最主要有两个阶段:一个map阶段,负责拆分;一个是reduce阶段,负责聚合
一个文件切块(Split)对应一个mapTask
mapreduce没有block的概念,默认一个切块(Split)对应block块的大小(128M)。
MapReduce处理的数据类型是<key,value>键值对
在这里插入图片描述


MapTask

运行流程

在这里插入图片描述

1. 读取数据组件 InputFormat(默认 TextInputFormat )会通过 getSplits 方法,对输入目录中的文件(输入目录也就是 TextInputFormat 的 Path )进行逻辑切片得到 splits
2. 将输入文件切分为splits之后,由 RecordReader 对象(默认 LineRecordReader )进行读取,以 \n 作为分隔符,读取一行数据,返回 <key1,value1>( Key1 表示每行首字符偏移值,value1 表示这一行文本内容)
3. 返回<key1,value1>后,进入用户自己继承的 Mapper 类中,执行用户重写的 map 函数。( RecordReader 读取一行这里调用一次)
4. map 逻辑完之后,将map的每条结果通过 context.write 进行 collect 数据收集。在 collect 中,会先对其进行分区处理,默认使用 HashPartitioner
5. 接下来,会将数据写入内存,内存中这片区域叫做环形缓冲区,缓冲区的作用是批量收集 map 结果减少磁盘 IO 的影响。<key,value> 对以及 Partition 的结果都会被写入缓冲区。当然写入之前,key 与 value 值都会被序列化成字节数组。

环形缓冲区其实是一个数组,数组中存放着 key、value 的序列化数据key、value 的元数据信息,包括 partition、key 的起始位置value 的起始位置以及 value 的长度
缓冲区是有大小限制,默认是 100MB。MapTask 输出结果过多可能会撑爆内存,所以需要在一定条件下将缓冲区中的数据临时写入磁盘,这种操作被称为 Spill,中文可译为溢写(这个溢写是由单独线程来完成,不影响往缓冲区写 map 结果的线程)。整个缓冲区有个溢写的比例 spill.percent(这个比例默认是0.8),不会阻止 map 的结果输出。当缓冲区的数据已经达到阈值(buffer size * spill percent = 100MB * 0.8 = 80MB),溢写线程启动,锁定这 80MB 的内存,执行溢写过程。Map task 的输出结果还可以往剩下的 20MB 内存中写,互不影响。(环形缓冲区的大小,涉及到 MapTask 的调优过程。如果内存充足,可以将环形缓冲区的大小调大。

6. 当溢写线程启动后,需要对这 80MB 空间内的 key 做排序(Sort)。排序是 MapReduce 模型默认的行为,这里的排序也是对序列化的字节做的排序。

如果设置了 Combiner ,会在排序后进行,Combiner 适用于 Reduce 的输入<key,value>对与输出<key,value>对类型完全一致,且不影响最终结果的场景,比如累加、最大值等。Combiner 会将有相同 key 的<key,value>对的 value 加起来,减少溢写到磁盘的数据量。

7. 合并溢写文件。每次溢写会在磁盘上生成一个临时文件(写之前判断是否有combiner),如果map的输出结果很大,就会有多次这样的溢写发生,磁盘上相应的也就会有多个临时文件存在。当整个数据处理结束之后开始对磁盘中的临时文件进行merge合并,因为最终的文件只有一个,写入磁盘,并且为这个文件提供了一个索引文件,以记录每个reduce对应数据的偏移量。


ReduceTask

运行流程

在这里插入图片描述

1. Copy 阶段。Reduce 进程启动一些数据 copy 线程 (Fetcher),通过 HTTP 方式请求 MapTask 获取属于自己的文件。
2. Merge 阶段。和 Map 端的 Merge 相同,只是存放的数值是不同 Map 端 Copy 过来的。

Copy 过来的数据会先放入内存缓冲区中(这里的缓冲区大小要比 map 端的更为灵活)。merge 有三种形式:内存到内存;内存到磁盘;磁盘到磁盘(默认情况下第一种形式不启用)。当内存中的数据量到达一定阈值,就启动内存到磁盘的 merge。与 map 端类似,这也是溢写的过程(这个过程中如果你设置有 Combiner,也是会启用的),然后在磁盘中生成了众多的溢写文件。第二种 merge 方式一直在运行,直到没有 map 端的数据时才结束,然后启动第三种磁盘到磁盘的 merge 方式生成最终的文件。

3. 合并排序。把分散的数据合并成一个大的数据后,还会再对合并后的数据排序。
4. 对排序后的键值对调用 reduce 方法。key 相等的键值<key,value>对调用一次 reduce 方法,每次调用会产生零个或者多个键值对,最后把这些输出的键值对写入到 HDFS 文件中。


Shuffle 是什么?

shuffle 就是指 map 阶段处理的数据如何传递给 reduce 阶段(就是分区、排序、规约和分组的操作),是 MapReduce 框架中最关键的一个流程

MapReduce 流程中有哪些 Shuffle 行为:
1. Collect 阶段。将 MapTask 的结果输出到默认大小为 100M 的环形缓冲区,保存的是 <key,value>Partition 分区信息等。
2. Spill 阶段。当内存中的数据量达到一定的阈值的时候,就会将数据写入本地磁盘,在将数据写入磁盘之前需要对数据进行一次排序的操作,如果配置了 combiner,还会将有相同分区号和 key 的数据进行排序。
3. Merge 阶段。把所有溢出的临时文件进行一次合并操作,以确保一个 MapTask 最终只产生一个中间数据文件。
4. Copy 阶段。ReduceTask 启动 Fetcher 线程到已经完成 MapTask 的节点上复制一份属于自己的数据,这些数据默认会保存在内存的缓冲区中,当内存的缓冲区达到一定的阀值的时候,就会将数据写到磁盘之上。
5. Merge 阶段。在 ReduceTask 远程复制数据的同时,会在后台开启两个线程对内存到本地的数据文件进行合并操作。
6. Sort 阶段。在对数据进行合并的同时,会进行排序操作,由于 MapTask 阶段已经对数据进行了局部的排序,ReduceTask 只需保证 Copy 的数据的最终整体有效性即可。


编写 Java API

步骤

可控的八个步骤
1.读取文件,解析成<key,value>对。  这里是<k1,v1>
2.接收<k1,v1>,自定义我们的map逻辑,然后转换成新的<k2,v2>进行输出,往下发送。  这里发送出去的是<k2,v2>
3.分区。相同key的value发送到同一个reduce里面去,key合并,value形成一个集合
4.排序。
5.规约。
6.分组。
7.接收<k2,v2>,自定义reduce逻辑,转换成新的<k3,v3>进行输出
8.将<k3,v3>进行输出
 

简单 API 实例

Mapper 类编写
/**
 * 需求: 对三个文本文件中的数字内容合并到一个文件中进行排序并添加序号
 *
 * <p>Map 阶段:把数字放到 key 的位置
 */
public class SecondMapper extends Mapper<LongWritable, Text, IntWritable, NullWritable> {
    @Override
    protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
        String num = value.toString();
        IntWritable k = new IntWritable(Integer.parseInt(num));
        context.write(k,NullWritable.get());
    }
}

 

Reduce 类编写
/**
 * 需求: 对三个文本文件中的数字内容合并到一个文件中进行排序并添加序号
 *
 * <p>
 * Reduce 阶段:对数字进行快排并添加序号
 * </p>
 */
public class SecondReducer extends Reducer<IntWritable, NullWritable, Text, NullWritable> {
  int count = 0;
  int tempNum = 0;

    /**
     * 使用快排实现需求
     * @param key
     * @param values
     * @param context
     * @throws IOException
     * @throws InterruptedException
     */
  @Override
  protected void reduce(IntWritable key, Iterable<NullWritable> values, Context context)
      throws IOException, InterruptedException {

    for (NullWritable value : values) {
      if (key.get() > tempNum) {
        count++;
        tempNum = key.get();
      }
      String k = count + "\t" + tempNum;
      Text result = new Text(k);
      context.write(result, NullWritable.get());
    }
  }
}

 

Driver 类编写
/**
 * 需求: 对三个文本文件中的数字内容合并到一个文件中进行排序并添加序号
 *
 * Driver 驱动类
 */
public class SecondDriver {
    public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
        // 获取 mr 配置
        Configuration conf = new Configuration();
        FileSystem fs = FileSystem.get(conf);
        // 封装到 job 实例中
        Job job = Job.getInstance(conf, "SecondAssignmentJob");

        // 指定本程序的jar包所在的本地路径,方便打包到集群运行
        job.setJarByClass(SecondDriver.class);

        // 获取输入路径
        FileInputFormat.setInputPaths(job, new Path("./input"));

        // 设置Mapper类
        job.setMapperClass(SecondMapper.class);
        // 设置Mapper类输出的k2,v2类型
        job.setMapOutputKeyClass(IntWritable.class);
        job.setMapOutputValueClass(NullWritable.class);

        // 设置Reducer类
        job.setReducerClass(SecondReducer.class);
        // 设置Reducer类输出的k3,v3类型
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(NullWritable.class);

        // 获取输出路径
        Path outputPath = new Path("./output/second");
        if (fs.exists(outputPath)) {
            fs.delete(outputPath, true);
        }
        FileOutputFormat.setOutputPath(job, outputPath);

        // 提交任务
        boolean flag = job.waitForCompletion(true);

        // 获取任务执行完成的状态
        int i = flag ? 0 : 1;

        System.exit(i);
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值