MapReduce的MapTask数量如何确定?

在技术领域,尤其是开源技术领域,版本变迁速度极快,有些介绍原理机制的文章可能并不能适合当前的技术版本,有时候找到一篇文章中关于某问题的解决方案,可能并不奏效,其原因何在?版本不同!

在文章中说清楚技术原理分析针对的版本是一件极其道德的事情。

本文论述的原理针对以下版本:

hadoop:2.7.3

1 问题

在进行map计算之前,MapReduce框架会根据输入文件计算输入数据分片(input split),每个数据分片针对一个map任务,数据分片存储的并非数据本身,而是一个分片长度和一个记录数据的位置的数组。

那么数据分成多少片(how much splits)是如何确定的呢?

2 MapTask数量影响因素

影响map个数(split个数)的主要因素有:

  1. 文件的大小。当块(dfs.block.size)为128m时,如果输入文件为128m,会被划分为1个split;当块为256m,会被划分为2个split。
  2. 文件的个数。FileInputFormat按照文件分割split,并且只会分割大文件,即那些大小超过HDFS块的大小的文件。如果HDFS中dfs.block.size设置为128m,而输入的目录中文件有100个,则划分后的split个数至少为100个。
  3. splitSize的大小。分片是按照splitszie的大小进行分割的,一个split的大小在没有设置的情况下,默认等于hdfs block的大小。
    已知以下参数

input_file_num : 输入文件的个数
block_size : hdfs的文件块大小,2.7.3默认为128M,可以通过hdfs-site.xml中的dfs.block.size参数进行设置
total_size : 输入文件整体的大小,由框架循环叠加计算

MapTask的数量计算原则为:
(1)默认map个数
如果不进行任何设置,默认的map个数是和blcok_size相关的。
default_num = total_size / block_size;
(2)自定义设置分片的minSize、maxSize
如果在MapReduce的驱动程序(main方法)中通过以下方法设置了分片的最小或最大的大小

//设置最小分片大小,单位byte
FileInputFormat.setMinInputSplitSize(job,1024*1024*10L); //10MB
//设置最大分片大小,单位byte
FileInputFormat.setMaxInputSplitSize(job,1024L); //1KB

应用程序可以通过数据分片的最大和最小大小两个参数来对splitsize进行调节,则计算方式就变成了

splitSize=Math.max(minSize, Math.min(maxSize, blockSize)

其中maxSize即方法setMaxInputSplitSize设置的值,minSize即方法·setMinInputSplitSize·设置的值。
其设置原则就是

要增加map的个数,调整maxSize<blockSize;
要减小map的个数,调整minSize>blockSize。

如何查看Job的数据分片数?

方式1:在客户端提交任务时可在日志中查看,如图
在这里插入图片描述
方式2:如果开启了Hadoop的History Server,可在UI界面中看到MapTask的数量。
在这里插入图片描述

3. MapTask数量确定的原理分析

先来了解两个概念
block块(数据块,物理划分)
block是HDFS中的基本存储单位,hadoop1.x默认大小为64M而hadoop2.x默认块大小为128M。文件上传到HDFS,就要划分数据成块,这里的划分属于物理的划分(实现机制也就是设置一个read方法,每次限制最多读128M的数据后调用write进行写入到hdfs),块的大小可通过 dfs.block.size配置。block采用冗余机制保证数据的安全:默认为3份,可通过dfs.replication配置。
注意:当更改块大小的配置后,新上传的文件的块大小为新配置的值,以前上传的文件的块大小为以前的配置值。

split分片(数据分片,逻辑划分)
Hadoop中split划分属于逻辑上的划分,目的只是为了让map task更好地获取数据。split是通过hadoop中的InputFormat接口中的getSplits()方法得到的。
splitSize由以下变量共同决定:
(1)totalSize:整个mapreduce job输入文件的总大小,框架循环计算输入目录下的所有文件的大小之和。

    long totalSize = 0;        // compute total size
    for (FileStatus file: files) { // check we have valid files
      if (file.isDirectory()) {
        throw new IOException("Not a file: "+ file.getPath());
      }
      totalSize += file.getLen();
    }

(2)minSize:数据分片的最小值,默认为1
(3)maxSize:数据分片的最大值,默认为Integer最大值,即Integer.MAX_VALUE

Hadoop2.7.3版本分片大小计算源码如下:
org.apache.hadoop.mapreduce.lib.input.FileInputFormat

long minSize = Math.max(getFormatMinSplitSize(), getMinSplitSize(job));
long maxSize = getMaxSplitSize(job);
long blockSize = file.getBlockSize();
long splitSize = computeSplitSize(blockSize, minSize, maxSize);
protected long computeSplitSize(long blockSize, long minSize,long maxSize) {
    return Math.max(minSize, Math.min(maxSize, blockSize));
  }

分片确定的临界问题

分片大小的数量一定是按照公式Math.max(minSize, Math.min(maxSize, blockSize))计算的吗?

可做以下试验:文件大小 297M(311349250),块大小128M
测试代码

FileInputFormat.setMinInputSplitSize(job, 301349250);   
FileInputFormat.setMaxInputSplitSize(job, 10000);

由上面分片公式算出分片大小为301349250, 比 311349250小, 理论应该为2个map, 但实际测试后Map个数为1, 这是为什么呢?
分片的计算中,会考虑空间利用问题,每次分出一个分片后,都会判断剩下的数据能否在一定的比率(slop变量,默认10%)内压缩到当前分片中,如果不大于默认比率1.1,则会压缩到当前分片中。源码如下

private static final double SPLIT_SLOP = 1.1;   // 10% slop
long bytesRemaining = length;
while (((double) bytesRemaining)/splitSize > SPLIT_SLOP) {
    int blkIndex = getBlockIndex(blkLocations, length-bytesRemaining);
    splits.add(makeSplit(path, length-bytesRemaining, splitSize,blkLocations[blkIndex].getHosts(),blkLocations[blkIndex].getCachedHosts()));
    bytesRemaining -= splitSize;
}

4. 应用

4.1 MapTask数量设置不当带来的问题

Mapper数据过大的话,会产生大量的小文件,过多的Mapper创建和初始化都会消耗大量的硬件资源 。
Mapper数太小,并发度过小,Job执行时间过长,无法充分利用分布式硬件资源。

4.2MapTask数量控制方法

(1)如果想增加map个数,则通过FileInputFormat.setMaxInputSplitSize(job,long bytes)方法设置最大数据分片大小为一个小于默认blockSize的值,越小map数量越多。
(2)如果想减小map个数,则FileInputFormat.setMinInputSplitSize(job,long bytes) 方法设置最小数据分片大小为一个大于默认blockSize的值,越大map数量越少。
(3)如果输入中有很多小文件,依然想减少map个数,则需要将小文件使用HDFS提供的API合并为大文件,然后使用方法2。

5. ReduceTask的数量如何确定?

Reduce任务是一个数据聚合的步骤,数量默认为1。使用过多的Reduce任务则意味着复杂的shuffle,并使输出文件数量激增。而reduce的个数设置相比map的个数设置就要简单的多,只需要设置在驱动程序中通过job.setNumReduceTasks(int n)即可。

  • 14
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值