MapReduce框架原理之(二)MapReduce工作流程


在这里插入图片描述

MapReduce工作流程

1. 流程图

  1. MapReduce流程图(1)
    在这里插入图片描述
  2. MapReduce流程图(2)
    在这里插入图片描述

2. 流程详解

MapReduce执行机制
这里只是指出个人理解的部分,帮助记忆MapReduce的工作流程,实际上细节还有很多,有不妥的地方还请多多指教.

实际上,我们在Driver调用了job.waitForCompletion后,客户端并不是马上将job提交给YARN,在向YARN提交job之前,客户端会先通过反射,获取到job将要使用到的InputFormat,以获得逻辑的切片规则,并将切片规则记录到本地的文件中:
windowns端的话在C:\tmp\hadoop-PC_NAME\mapred\staging\PC_NAMEJOBID\.staging\job_localJOBID目录下(执行完毕后会被删除)
在这里插入图片描述
注:InputFormat只是进行逻辑切片规则的指定,而不是真正的进行物理切片,真正的物理切片动作是再RecordReader中进行的
制定好切片规则后(当然这个过程很复杂,还有很多其他步骤),会将这些job文件(job.xml等)提交给YARN,然后YARN会根据切片规则等信息分配出相应的MapTask,在MapTask阶段通过RecordReader来进行真正的文件切分(物理上的),并将切片后的数据制作成K,V对的形式,交给Mapper的map方法,一般来讲一个切片的数据会启用一个MapTask,在MapTask和ReduceTask中还有一步shuffle的操作,这个先略过,后面会进行说明.经过MapTask阶段的处理,数据来到了ReduceTask,并且一般来讲,相同Key的数据会交给同一个ReduceTask来处理,在ReduceTask中的reduce方法里,写了我们的业务代码,将数据封装成我们需要的格式后输出到目标目录,输出时会根据所指定的OutputFormatClass类型,来对数据进行输出格式化.

3. shuffle机制

在这里插入图片描述
上面提到过shuffle,它在MapTask和ReduceTask中都有存在.

3.1 MapTask中:

  1. 从map方法通过context.write(key, value)输出出来的K,V数据,会被写入到内存的环形缓冲区,它的默认大小为100M,在其中某一点作为起点,顺时针方向写入数据,逆时针方向写入数据对应的索引等元数据信息.(在环状缓冲区内会进行分区,和第1次排序,快排
  2. 每当写入的数据达到环状缓冲区80%的容量时,会发生溢出操作,将数据持久到磁盘,根据数据的量大小,会生成N个溢出文件.
  3. 这些文件会被合并成大的溢出文件交给Mapper.(第2次排序,归并排序
  4. 在溢出和合并的过程中都会调用Partitioner进行分区并同时针对Key进行排序,默认的分区数总是0.

3.2 ReduceTask中:

  1. ReduceTask根据自己的分区号,去MapTask机器上找相应的结果分区数据.
  2. ReduceTask会取到分布在不同MapTask上的同一个分区的数据,ReduceTask会将这些文件再进行合并(第3次排序,归并排序
  3. 行成大文件后,Shuffle的过程就结束了,后面进入ReduceTask的逻辑运算过程(从文件中取出一个一个的键值对Group,调用reduce方法)

Shuffle环形缓冲区大小会影响到MapReduce的执行效率,原则上说,缓冲区越大磁盘IO的次数就越少,执行速度越快. 缓冲区的大小可以通过调整,io.sort.mb来进行修改,默认是100M

3.3 Partition分区:

  1. 问题引出
    要求将统计结果按条件输出到不同文件中(分区),比如将统计结果按手机归属地不同省份输出到不同文件中.
  2. 默认Partitioner分区:
protected int getPartition(int hash, int numReduceTasks) {
   
    return (hash & 2147483647) % numReduceTasks;
}

mapred-default.xml

<property>
  <name>mapreduce.job.reduces</name>
  <value>1</value>
  <description>The default number of reduce tasks per job. Typically set to 99%
  of the cluster's reduce capacity, so that if a node fails the reduces can
  still be executed in a single wave.
  Ignored when mapreduce.framework.name is "local".
  </description>
</property>

在Java中,Integer的值用32进制来表示的,而2147483647代表Integer的最大值.这里用hash值和Integer的最大值做位与运算,最大值总是01111111…即首位是0,其余位置都是1,任何正数与值进行位与计算,结果都是其本身,而负数则被转换成正数.
而默认的numReduceTasks是1,所以mod结果总是0,即代表默认的只有一个ReduceTask,并且对数据不进行分区.

  1. 自定义Partitioner步骤
    (1)自定义类继承Partitioner,重写getPartition()方法
    (2)在Driver类中设置自定义的PartitionerClass
    job.setPartitionerClass(xxxxx.class);
    (3)自定义Parititioner后要根据其逻辑设置相应的ReduceTastk
    job.setNumReduceTasks(5);

  2. 分区总结
    (1)如果ReduceTask的数量 > getPartition的结果数,则最终生成的文件会多产生几个part-r-000xx的空文件;
    (2)如果1 < ReduceTask的数量 < getPartition的结果数,则有一部分分区数据无处安放,则会抛异常;
    (3)如果ReduceTask的数量 = 1,则不管MapTask端输出多少个分区文件,最终结果都交给这一个ReduceTask来处理,最终也只产生一个part-r-00000文件;
    (4)分区号必须从0开始逐一累加.
    e.g:
    假设自定义分区数为5,则:
    <1> job.setNumReduceTasks(1); 会正常运行,只不过只产生一个输出文件
    <2> job.setNumReduceTasks(2);会报错
    <3> job.setNumRedeceTasks(6);会正常执行,只不过会产生6个输出文件,其中一个是空的

  3. 实操演练
    (1)需求,将手机按号段输出到不同文件, 136、137、138、139分别放到4个文件,其他号段放一个文件中.
    (2)业务代码

    基于我的另一篇有关Hadoop序列化的博客中的demo,来进行演示,如果对序列化相关内容感兴趣可以参考Hadoop之序列化
    自定义Partitioner

package partition;

import cstmbean.FlowBean;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Partitioner;

public class PhonePartitioner extends Partitioner<Text, FlowBean> {
   
    @Override
    public int getPartition(Text text, FlowBean flowBean, int i) {
   
        String code = text.toString().substring(0, 3);
        int partition = 0;
        switch(code) {
   
            case "136":
                partition = 0;
                break;
            case "137":
                partition = 1;
                break;
            case "138":
                partition = 2;
                break;
            case "139":
                partition = 3;
                break;
            default:
                partition = 4;
        }
        return partition;
    }
}

Driver类中

job.setPartitionerClass(PhonePartitioner.class);
job.setNumReduceTasks(5)
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值