专栏首页米虫的家BigData--MapReduce进阶(二)之工作机制

BigData--MapReduce进阶(二)之工作机制

MapReduce进阶

一、MapReduce工作流程

1、工作流程(1)

2、工作流程(2)

shuffle是从第七步开始的到第十六步结束的,下面是shuffle过程详解

  • 1)MapTask收集我们的map()方法输出的kv对,放到内存缓冲区中
  • 2)从内存缓冲区不断溢出本地磁盘文件,可能会溢出多个文件
  • 3)多个溢出文件会被合并成大的溢出文件
  • 4)在溢出过程及合并的过程中,都要调用Partitioner进行分区和针对key进行排序
  • 5)ReduceTask根据自己的分区号,去各个MapTask机器上取相应的结果分区数据
  • 6)ReduceTask会取到同一个分区的来自不同MapTask的结果文件,ReduceTask会将这些文件再进行合并(归并排序)
  • 7)合并成大文件后,Shuffle的过程也就结束了,后面进入ReduceTask的逻辑运算过程(从文件中取出一个一个的键值对Group,调用用户自定义的reduce()方法)

二、Shuffle机制

Map方法之后,Reduce方法之前的数据处理过程称之为Shuffle。

1、Shuffle机制流程图

2、Partition分区

1)自定义Partitioner步骤
  • (1)自定义类继承Partitioner,重写getPartition()方法

java

package cn.buildworld.mapreduce.partition;

import cn.buildworld.mapreduce.flow.FlowBean;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Partitioner;

/**
 * @author MiChong
 * @date 2020-05-27 14:40
 */
public class MyPartitioner extends Partitioner<Text, FlowBean> {
    @Override
    public int getPartition(Text text, FlowBean flowBean, int numPartitions) {
        String phone = text.toString();
        switch (phone.substring(0, 3)) {
            case "136":
                return 0;
            case "137":
                return 1;
            case "138":
                return 2;
            case "139":
                return 3;
            default:
                return 4;
        }
    }
}
  • (2)在Job驱动中,设置自定义Partitioner

java

job.setPartitionerClass(MyPartitioner.class);
  • (3)自定义Partition后,要根据自定义Partitioner的逻辑设置相应数量的ReduceTask

java

job.setNumReduceTasks(5);
2)分区总结
  • (1)如果ReduceTask的数量> getPartition的结果数,则会多产生几个空的输出文件part-r-000xx;
  • (2)如果1<ReduceTask的数量<getPartition的结果数,则有一部分分区数据无处安放,会Exception;
  • (3)如果ReduceTask的数量=1,则不管MapTask端输出多少个分区文件,最终结果都交给这一个ReduceTask,最终也就只会产生一个结果文件 part-r-00000;
  • (4)分区号必须从零开始,逐一累加。

3、WritableComparable排序

Bean对象实现WritableComparable几口,重写compareTo()方法

java

package cn.buildworld.mapreduce.writablecomparable;

import org.apache.hadoop.io.WritableComparable;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;

/**
 * @author MiChong
 * @date 2020-05-24 18:15
 */

/**
 * 必须实现Writable接口
 */
public class FlowBean implements WritableComparable<FlowBean> {
    private long upFlow;
    private long downFlow;
    private long sumFlow;

    // 此处实现自定义排序
    @Override
    public int compareTo(FlowBean o) {
        return Long.compare(o.sumFlow, this.sumFlow);
    }
}

4、Combiner合并

  • (1)Combiner是MR程序中Mapper和Reducer之外的一种组件。
  • (2)Combiner组件的父类就是Reducer。
  • (3)Combiner和Reducer的区别在于运行的位置 Combiner是在每一个MapTask所在的节点运行; Reducer是接收全局所有Mapper的输出结果;
  • (4)Combiner的意义就是对每一个MapTask的输出进行局部汇总,以减小网络传输量。
  • (5)Combiner能够应用的前提是不能影响最终的业务逻辑,而且,Combiner的输出kv应该跟Reducer的输入kv类型要对应起来。
实现步骤

自定义一个Combiner继承Reducer,重写Reduce方法

java

public class WordcountCombiner extends Reducer<Text, IntWritable, Text,IntWritable>{
	@Override
	protected void reduce(Text key, Iterable<IntWritable> values,Context context) throws IOException, InterruptedException {


 // 1 汇总操作
    int count = 0;
	for(IntWritable v :values){
		count += v.get();
	}
   // 2 写出
		context.write(key, new IntWritable(count));
	}

在Job驱动类中设置

java

job.setCombinerClass(WordcountCombiner.class);

5、GroupingComparator分组

  • (1)自定义类继承WritableComparator
  • (2)重写compare()方法
  • (3)创建一个构造将比较对象的类传给父类

java

package cn.buildworld.mapreduce.groupCompa;

import org.apache.hadoop.io.WritableComparable;
import org.apache.hadoop.io.WritableComparator;

/**
 * @author MiChong
 * @date 2020-05-30 13:44
 */
public class OrderComparator extends WritableComparator {

    protected OrderComparator() {
        super(OrderBean.class, true);
    }

    @Override
    public int compare(WritableComparable a, WritableComparable b) {

        OrderBean oa = (OrderBean) a;
        OrderBean ob = (OrderBean) b;

        return oa.getOrderId().compareTo(ob.getOrderId());

    }
}

三、MapTask工作机制

  • (1)Read阶段:MapTask通过用户编写的RecordReader,从输入InputSplit中解析出一个个key/value。
  • (2)Map阶段:该节点主要是将解析出的key/value交给用户编写map()函数处理,并产生一系列新的key/value。
  • (3)Collect收集阶段:在用户编写map()函数中,当数据处理完成后,一般会调用OutputCollector.collect()输出结果。在该函数内部,它会将生成的key/value分区(调用Partitioner),并写入一个环形内存缓冲区中。
  • (4)Spill阶段:即“溢写”,当环形缓冲区满后,MapReduce会将数据写到本地磁盘上,生成一个临时文件。需要注意的是,将数据写入本地磁盘之前,先要对数据进行一次本地排序,并在必要时对数据进行合并、压缩等操作。

溢写阶段详情:

​ 步骤1:利用快速排序算法对缓存区内的数据进行排序,排序方式是,先按照分区编号Partition进行排序,然后按照key进行排序。这样,经过排序后,数据以分区为单位聚集在一起,且同一分区内所有数据按照key有序。

​ 步骤2:按照分区编号由小到大依次将每个分区中的数据写入任务工作目录下的临时文件output/spillN.out(N表示当前溢写次数)中。如果用户设置了Combiner,则写入文件之前,对每个分区中的数据进行一次聚集操作。

​ 步骤3:将分区数据的元信息写到内存索引数据结构SpillRecord中,其中每个分区的元信息包括在临时文件中的偏移量、压缩前数据大小和压缩后数据大小。如果当前内存索引大小超过1MB,则将内存索引写到文件output/spillN.out.index中。

  • 5)Combine阶段:当所有数据处理完成后,MapTask对所有临时文件进行一次合并,以确保最终只会生成一个数据文件。

​ 当所有数据处理完后,MapTask会将所有临时文件合并成一个大文件,并保存到文件output/file.out中,同时生成相应的索引文件output/file.out.index。

​ 在进行文件合并过程中,MapTask以分区为单位进行合并。对于某个分区,它将采用多轮递归合并的方式。每轮合并io.sort.factor(默认10)个文件,并将产生的文件重新加入待合并列表中,对文件排序后,重复以上过程,直到最终得到一个大文件。

​ 让每个MapTask最终只生成一个数据文件,可避免同时打开大量文件和同时读取大量小文件产生的随机读取带来的开销。

四、ReduceTask工作机制

1、工作机制

  • (1)Copy阶段:ReduceTask从各个MapTask上远程拷贝一片数据,并针对某一片数据,如果其大小超过一定阈值,则写到磁盘上,否则直接放到内存中。
  • (2)Merge阶段:在远程拷贝数据的同时,ReduceTask启动了两个后台线程对内存和磁盘上的文件进行合并,以防止内存使用过多或磁盘上文件过多。
  • (3)Sort阶段:按照MapReduce语义,用户编写reduce()函数输入数据是按key进行聚集的一组数据。为了将key相同的数据聚在一起,Hadoop采用了基于排序的策略。由于各个MapTask已经实现对自己的处理结果进行了局部排序,因此,ReduceTask只需对所有数据进行一次归并排序即可。
  • (4)Reduce阶段:reduce()函数将计算结果写到HDFS上。

2、设置ReduceTask并行度(个数)

educeTask的并行度同样影响整个Job的执行并发度和执行效率,但与MapTask的并发数由切片数决定不同,ReduceTask数量的决定是可以直接手动设置:

java

// 默认值是1,手动设置为4
job.setNumReduceTasks(4);

五、OutputFormat数据输出

1、OutputFormat接口实现类

  • 文本输出TextOutputFormat 默认的输出格式是TextOutputFormat,它把每条记录写为文本行。它的键和值可以是任意类型,因为TextOutputFormat调用toString()方法把它们转换为字符串。
  • SequenceFileOutputFormat 将SequenceFileOutputFormat输出作为后续 MapReduce任务的输入,这便是一种好的输出格式,因为它的格式紧凑,很容易被压缩。
  • 自定义OutputFormat

根据用户需求,自定义实现输出。

2、自定义OutputFormat使用场景及步骤

  • (1)自定义一个类继承FileOutputFormat。
  • (2)改写RecordWriter,具体改写输出数据的方法write()。

java

package cn.buildworld.mapreduce.outputformat;

import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.RecordWriter;
import org.apache.hadoop.mapreduce.TaskAttemptContext;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

import java.io.IOException;

/**
 * @author MiChong
 * @date 2020-05-31 16:45
 */
public class MyRecordWriter extends RecordWriter<LongWritable, Text> {

    private FSDataOutputStream atguigu;
    private FSDataOutputStream other;

    /**
     * 初始化方法
     */
    public void initialize(TaskAttemptContext job) throws IOException {
        String dir = job.getConfiguration().get(FileOutputFormat.OUTDIR);
        FileSystem fileSystem = FileSystem.get(job.getConfiguration());
        atguigu = fileSystem.create(new Path(dir+"/my.log"));
        other = fileSystem.create(new Path(dir+"/others.log"));

    }

    /**
     * 将KV写出,每对KV调用一次
     *
     * @param key
     * @param value
     * @throws IOException
     * @throws InterruptedException
     */
    @Override
    public void write(LongWritable key, Text value) throws IOException, InterruptedException {
        String out = value.toString() + "\n";
        if (out.contains("atguigu")) {
            atguigu.write(out.getBytes());
        } else {
            other.write(out.getBytes());
        }
    }

    @Override
    public void close(TaskAttemptContext context) throws IOException, InterruptedException {
        IOUtils.closeStream(atguigu);
        IOUtils.closeStream(other);
    }
}

六、Join应用

1、Reduce Join

2、Map Join

  • 1.使用场景

Map Join适用于一张表十分小、一张表很大的场景。

  • 2.优点

思考:在Reduce端处理过多的表,非常容易产生数据倾斜。怎么办?

在Map端缓存多张表,提前处理业务逻辑,这样增加Map端业务,减少Reduce端数据的压力,尽可能的减少数据倾斜。

  • 3.具体办法:采用DistributedCache

(1)在Mapper的setup阶段,将文件读取到缓存集合中。

(2)在驱动函数中加载缓存。

java

import java.net.URI;
	
// 缓存普通文件到Task运行节点。
job.addCacheFile(new URI("file:///e:/cache/pd.txt"));

七、MapReduce开发总结

1.输入数据接口:InputFormat

  • (1)默认使用的实现类是:TextInputFormat
  • (2)TextInputFormat的功能逻辑是:一次读一行文本,然后将该行的起始偏移量作为key,行内容作为value返回。
  • (3)KeyValueTextInputFormat每一行均为一条记录,被分隔符分割为key,value。默认分隔符是tab(\t)。
  • (4)NlineInputFormat按照指定的行数N来划分切片。
  • (5)CombineTextInputFormat可以把多个小文件合并成一个切片处理,提高处理效率。
  • (6)用户还可以自定义InputFormat

2.逻辑处理接口:Mapper

用户根据业务需求实现其中三个方法:map() setup() cleanup ()

3.Partitioner分区

  • (1)有默认实现 HashPartitioner,逻辑是根据key的哈希值和numReduces来返回一个分区号;key.hashCode()&Integer.MAXVALUE % numReduces
  • (2)如果业务上有特别的需求,可以自定义分区。

4.Comparable排序

  • (1)当我们用自定义的对象作为key来输出时,就必须要实现WritableComparable接口,重写其中的compareTo()方法。
  • (2)部分排序:对最终输出的每一个文件进行内部排序。
  • (3)全排序:对所有数据进行排序,通常只有一个Reduce。
  • (4)二次排序:排序的条件有两个。

5.Combiner合并

Combiner合并可以提高程序执行效率,减少IO传输。但是使用时必须不能影响原有的业务处理结果。

6.Reduce端分组:GroupingComparator

Reduce端对key进行分组。应用于:在接收的key为bean对象时,想让一个或几个字段相同(全部字段比较不相同)的key进入到同一个reduce方法时,可以采用分组排序。

7.逻辑处理接口:Reducer

用户根据业务需求实现其中三个方法:reduce() setup() cleanup ()

8.输出数据接口:OutputFormat

  • (1)默认实现类是TextOutputFormat,功能逻辑是:将每一个KV对,向目标文本文件输出一行。
  • (2)将SequenceFileOutputFormat输出作为后续 MapReduce任务的输入,这便是一种好的输出格式,因为它的格式紧凑,很容易被压缩。
  • (3)用户还可以自定义OutputFormat。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Mac做java开发(四):​hadoop分布式环境搭建

    大数据时代,分布式技术至关重要,因此,这篇文章介绍hadoop分布式环境搭建,作为个人学习大数据技术的实验环境。

    用户5473628
  • MapReduce之MapTask工作机制

    MapTask:map----->sort map:Mapper.map()中将输出的key-value写出之前 sort:Mapper.map()中将输出...

    孙晨c
  • 分布式计算—MapReduce、Spark、Storm、Flink分别适用什么场景

    链接:https://www.zhihu.com/question/403840013/answer/1317631316

    大数据老哥
  • Sqoop学习之路

    Sqoop (SQL to Hadoop) 是Apache顶级项⽬,官⽹地址:http://sqoop.apache.org.

    白贺
  • Hadoop-HA高可用搭建

    2、hosts文件要配置好,三台都可以相互通过主机名ping通,三台虚拟机都要配置!如下:

    可爱见见
  • [大数据架构 ]Apache大数据项目目录

    在使用BigData大约8年以上之后,我遇到了大量的项目。Esp Apache的运动对于BigData域非常强大。每个人都会提出一个针对特定解决方案的项目。但是...

    首席架构师智库
  • 超详细步骤!整合Apache Hudi + Flink + CDH

    使用Idea打开Hudi项目,更改packging/hudi-flink-bundle的pom.xml文件,修改flink-bundle-shade-hive2...

    ApacheHudi
  • 【Spark】Spark SQL原理、编译、配置及运行方式详述

    (1)在Hadoop中运行SQL的工具 在Hadoop中运行SQL的工具有Hive、Impala、Apache Drill、Presto、Spark SQL等...

    魏晓蕾
  • 大数据学习必备 | 推荐几个牛X 的 github 项目,助你事半功倍

    大家好,我是 梦想家 Alex 。我们都知道 github 对于程序员们而言,就是一个巨大的“聚宝盆”,上面不仅有很多优质的开源项目,还有很多热...

    大数据梦想家
  • SAP HANA神话(6):一场大撕逼

    我们继续聊database的故事。严格的说今天这篇有点偏题了。因为只有酱油主Michael Stonebraker,和DoNotEvil但是比谁都Evil的Go...

    用户1564362
  • 大数据的那些事(1):Google的后悔药

    新坑,鉴于工作越来越忙,填坑速度会慢一些,一周争取两到三次更新吧。 大数据这个概念红红火火的也有两三个年头了,我在这个坑里的时间可能要更长一些,勉强可以从08...

    用户1564362
  • 2019精炼的大数据技术学习路线

    近年来大数据BigData、人工智能AI、物联网Iot等行业发展迅猛,很多人都想要从事大数据技术开发工作,但是,请问要怎么做,路线是什么?从哪里开始学?学哪些?...

    用户2292346
  • 最新Hive的高频面试题新鲜出炉了!

    如果其中有一张表为小表,直接使用map端join的方式(map端加载小表)进行聚合。

    大数据老哥
  • 最新Hadoop的面试题总结

      1)Zookeeper:是一个开源的分布式应用程序协调服务,基于zookeeper可以实现同步服务,配置维护,命名服务。   2)Flume:一个高可用的,...

    大数据老哥
  • java转大数据的学习路线

    大数据本质也是数据,但是又有了新的特征,包括数据来源广、数据格式多样化(结构化数据、非结构化数据、Excel文件、文本文件等)、数据量大(最少也是TB级别的、甚...

    一起学习大数据
  • 大数据入门:MapReduce基本原理

    在围绕Hadoop形成的大数据技术生态当中,MapReduce的地位,在早期是处于核心地位的,但是伴随着数据处理实时性需求的不断提升,更多新的计算框架出现,Ma...

    成都加米谷大数据
  • 大数据学习带你了解Hadoop如何高效处理大数据

    Hadoop与Google一样,都是小孩命名的,是一个虚构的名字,没有特别的含义。从计算机专业的角度看,Hadoop是一个分布式系统基础架构,由Apache基金...

    用户2292346
  • 深入浅出学大数据(四)MapReduce快速入门及其编程实践

    此系列主要为我的学弟学妹们所创作,在某些方面可能偏基础。如果读者感觉较为简单,还望见谅!如果文中出现错误,欢迎指正~

    不温卜火
  • 【Spark】Spark Local 及 Spark On Standalone 环境搭建

    (1)解压spark安装包 $ cd /opt/softwares/cdh cdh]$ tar -zxf spark-1.6.1-bin-2.5.0-cdh...

    魏晓蕾

扫码关注云+社区

领取腾讯云代金券