此文紧接Job流程:提交MR-Job过程。上一篇分析可以看出,MR-Job提交过程的核心代码在于 JobSubmitter 类的 submitJobInternal()方法。本文就由此方法的这一句代码开始分析:

//计算并确定 map 的个数,以及各个输入切片 Splits 的相关信息
int maps = writeSplits(job, submitJobDir);

1.查看writeSplits()方法的实现过程:

private int writeSplits(org.apache.hadoop.mapreduce.JobContext job,
Path jobSubmitDir) throws IOException,
InterruptedException, ClassNotFoundException {
JobConf jConf = (JobConf)job.getConfiguration();
int maps;
if (jConf.getUseNewMapper()) { //决定map个数的关键性方法
maps = writeNewSplits(job, jobSubmitDir);
} else {
maps = writeOldSplits(jConf, jobSubmitDir);
}
//返回map个数
return maps;
}

2.查看writeNewSplits()方法的实现过程:

//此方法返回int类型,即map的个数
//此方法实现倒着分析为好
private <T extends InputSplit>
int writeNewSplits(JobContext job, Path jobSubmitDir) throws IOException,
InterruptedException, ClassNotFoundException {
Configuration conf = job.getConfiguration();
InputFormat<?, ?> input =
ReflectionUtils.newInstance(job.getInputFormatClass(), conf); //List集合是由getSplits()方法生成 -->【核心代码】
List<InputSplit> splits = input.getSplits(job);


//array数组是由List<InputSplit>集合转化而来 -->查看List集合的定义
T[] array = (T[]) splits.toArray(new InputSplit[splits.size()]); // sort the splits into order based on size, so that the biggest
// go first
Arrays.sort(array, new SplitComparator());
JobSplitWriter.createSplitFiles(jobSubmitDir, conf,
jobSubmitDir.getFileSystem(conf), array); //array数组的长度,即map的个数 -->查看array数组的定义
return array.length;
}

3.查看getSplits()方法的实现

  此方法是InputFormat 类的一个抽象方法。在其子类 FileInputFormat 类中为文件格式输入类型提供了统一的 getSplits()方法实现。

public List<InputSplit> getSplits(JobContext job) throws IOException {

//第一个参数返回值为 1;
//第二个参数是读取配置文件中的 mapreduce.input.fileinputformat.split.minsize 属性(默认值为 0),如果没有配置则返回 1.
//所以 minSize=Math(1,0),即值是 1
long minSize = Math.max(getFormatMinSplitSize(), getMinSplitSize(job)); //读取配置文件中的 mapreduce.input.fileinputformat.split.maxsize 属性(默认没有配置)
//如果没有配置则返回 long.MAX_VALUE
long maxSize = getMaxSplitSize(job); //定义 List 集合,用来存储输入分片 InputSplit
List<InputSplit> splits = new ArrayList<InputSplit>();

//变量 files 存储的是 "输入路径中所有的文件集合"
List<FileStatus> files = listStatus(job);

//循环处理每一个输入文件
for (FileStatus file: files) {

//获得文件路径
Path path = file.getPath();

//获得文件总长度
long length = file.getLen();

//判断文件是否为空
if (length != 0) {
BlockLocation[] blkLocations;
if (file instanceof LocatedFileStatus) {

//获得文件对应的 所有Block块的 位置
blkLocations = ((LocatedFileStatus) file).getBlockLocations();
} else {
FileSystem fs = path.getFileSystem(job.getConfiguration());
blkLocations = fs.getFileBlockLocations(file, 0, length);
}

//如果文件大小非空,并且文件允许被分割
if (isSplitable(job, path)) {

//获取配置文件中Block块的大小,默认128MB
long blockSize = file.getBlockSize(); //计算输入切片的大小【核心代码】
long splitSize = computeSplitSize(blockSize, minSize, maxSize); //将bytesRemaining(剩余未分片字节数)设置为整个文件的长度
long bytesRemaining = length;

//while()循环体,按照 splitSize 对每个输入文件进行【逻辑切分】,得到 Splits 集合
while (((double) bytesRemaining)/splitSize > SPLIT_SLOP) {
int blkIndex = getBlockIndex(blkLocations, length-bytesRemaining);

//参数列表:文件所在路径、切片起始的位置、切片大小、切片所在节点
splits.add(makeSplit(path, length-bytesRemaining, splitSize,
blkLocations[blkIndex].getHosts()));
bytesRemaining -= splitSize;
}
//如果block中剩下的一小段数据量小于splitSize,还是认为它是独立的分片
if (bytesRemaining != 0) {
int blkIndex = getBlockIndex(blkLocations, length-bytesRemaining);
splits.add(makeSplit(path, length-bytesRemaining, bytesRemaining,
blkLocations[blkIndex].getHosts()));
}
} else { //文件不能切分则将整个文件作为一个输入分片 InputSplit
splits.add(makeSplit(path, 0, length, blkLocations[0].getHosts()));
}
} else {
//输入文件为空,则对应的 Block块 所在节点也应该为空
splits.add(makeSplit(path, 0, length, new String[0]));
}
}
// Save the number of input files for metrics/loadgen
job.getConfiguration().setLong(NUM_INPUT_FILES, files.size());
LOG.debug("Total # of splits: " + splits.size());
return splits;
}

4.查看computeSplitSize()方法的具体实现:

protected long computeSplitSize(long blockSize, long minSize,
long maxSize) {

//对于默认情况,三个参数分别为:1,long.MAX_VALUE,128MB
//所以,表达式整体返回 128MB
return Math.max(minSize, Math.min(maxSize, blockSize));
}

所以,影响 map 任务数量的因素在于以上三个参数的设置:

  • 默认情况 splitSize=blockSize,即一个 map 任务读取一个 block 块。
  • 增加 minSize 超过 128M,则增加 splitSize,即 map 任务个数变小。
  • 减小 maxSize 小于 128M,则减小 splitSize,即 map 任务个数变多。

Map 个数 = 文件大小 / 128M       Reduce 个数 = 分区 Partitioner 个数 = 最终输出文件个数

注意:特殊压缩的 map 切分【即不可切分文件】
例题:假设HDFS上有一个大小75MB的文件,当客户端设置Block大小为64MB。则运行MR任务读取该文件时InputSplit大小为多少?
1) 如果该文件是普通文件,则应该是两个InputSplit分片:64MB 和 11MB。
2) 如果该文件是 gzip等压缩包格式的文件,则只有一个InputSplit分片:75MB。
  

Job流程:决定map个数的因素的更多相关文章

  1. hadoop控制map个数(转)

    原文链接:https://blog.csdn.net/lylcore/article/details/9136555     hadooop提供了一个设置map个数的参数mapred.map.task ...

  2. hadoop之 map个数控制

    hadooop提供了一个设置map个数的参数mapred.map.tasks,我们可以通过这个参数来控制map的个数.但是通过这种方式设置map的个数,并不是每次都有效的.原因是mapred.map. ...

  3. Hadoop框架下MapReduce中的map个数如何控制

    控制map个数的核心源码 long minSize = Math.max(getFormatMinSplitSize(), getMinSplitSize(job)); //getFormatMinS ...

  4. MapReduce中的map个数

    在map阶段读取数据前,FileInputFormat会将输入文件分割成split.split的个数决定了map的个数.影响map个数(split个数)的主要因素有: 1) 文件的大小.当块(dfs. ...

  5. MapReduce的map个数调节 与 Hadoop的FileInputFormat的任务切分原理

    在对日志等大表数据进行处理的时候需要人为地设置任务的map数,防止因map数过小导致集群资源被耗光.可根据大表的数据量大小设置每个split的大小. 例如设置每个split为500M: set map ...

  6. hadoop map 个数 源码分析

    本文转自http://ronxin999.blog.163.com/blog/static/42217920201279112163/

  7. Job流程:Mapper类分析

    此文紧接Job流程:决定map个数的因素,Map任务被提交到Yarn后,被ApplicationMaster启动,任务的形式是YarnChild进程,在其中会执行MapTask的run()方法.无论是 ...

  8. Job流程:提交MR-Job过程

    1.一个标准 MR-Job 的执行入口: //参数 true 表示检查并打印 Job 和 Task 的运行状况 System.exit(job.waitForCompletion(true) ? 0 ...

  9. 如何在hadoop中控制map的个数

    hadooop提供了一个设置map个数的参数mapred.map.tasks,我们可以通过这个参数来控制map的个数.但是通过这种方式设置map的个数,并不是每次都有效的.原因是mapred.map. ...

随机推荐

  1. Linux系统时间快8个小时

    1.vi /etc/sysconfig/clock   #编辑文件ZONE="Asia/Shanghai"UTC=false                          #设 ...

  2. .c和.h的联系

    .c文件就是C语言系列的源文件,而H文件则是C语言的头文件,即C系列中存放函数和全局变量的文件,因为C中的函数是被封装起来的,即无法看到其代码. 子程序不要定义在*.h中.函数定义要放在*.c中,而* ...

  3. $$wname

    w变量名为变量,减少重复代码. <?php function w_w($w_arr, $link) { $wres = true; foreach ($w_arr AS $w) { $wname ...

  4. 剑指Offer——按之字形顺序打印二叉树

    题目描述: 请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推. 分析: 我们都知道二叉树的层次遍历用的是队 ...

  5. linux知识体系

    0. Linux简介与厂商版本 1. Linux开机启动 2. Linux文件管理 3. Linux的架构 4. Linux命令行与命令 5. Linux文件管理相关命令 6. Linux文本流 7. ...

  6. Python高级特性(3): Classes和Metaclasses(转)

    原文:Python高级特性(3): Classes和Metaclasses 类和对象 类和函数一样都是Python中的对象.当一个类定义完成之后,Python将创建一个“类对象”并将其赋值给一个同名变 ...

  7. Python并行编程(十):多线程性能评估

    1.基本概念 GIL是CPython解释器引入的锁,GIL在解释器层面阻止了真正的并行运行.解释器在执行任何线程之前,必须等待当前正在运行的线程释放GIL,事实上,解释器会强迫想要运行的线程必须拿到G ...

  8. Flask之flask-script

    简介 Flask-Scropt插件为在Flask里编写额外的脚本提供了支持.这包括运行一个开发服务器,一个定制的Python命令行,用于执行初始化数据库.定时任务和其他属于web应用之外的命令行任务的 ...

  9. Flask上下文管理

    一.一些python的知识 1.偏函数 def add(x, y, z): print(x + y + z) # 原本的写法:x,y,z可以传任意数字 add(1,2,3) # 如果我要实现一个功能, ...

  10. CentOS yum 安装node.js

    第一步: curl --silent --location https://rpm.nodesource.com/setup_10.x | sudo bash - 第二步: sudo yum -y i ...