博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Hadoop旧mapreduce的map任务切分原理
阅读量:7297 次
发布时间:2019-06-30

本文共 4324 字,大约阅读时间需要 14 分钟。

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/beliefer/article/details/51395043

前言

最近在工作过程中接触一些Hive数据仓库中的表,这些表实际是从关系型数据库通过Sqoop抽到Hive的。在开发过程中对map任务的划分进行性能调优,发现mapreduce中关于FileInputFormat的参数调整都不起作用,最后发现这些老任务都是用旧版的mapreduce开发的,于是顺便研究下旧版mapreduce的任务划分策略。有关新版mapreduce的任务划分策略,大家可以参考我之前的博文《》。

源码分析

根据《》一文的内容,我们知道map任务的划分关键在于FileInputFormat的getSplits方法的实现策略,现在我们来看看其源码:

public InputSplit[] getSplits(JobConf job, int numSplits)    throws IOException {    Stopwatch sw = new Stopwatch().start();    FileStatus[] files = listStatus(job);        // Save the number of input files for metrics/loadgen    job.setLong(NUM_INPUT_FILES, files.length);    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();    }    long goalSize = totalSize / (numSplits == 0 ? 1 : numSplits);    long minSize = Math.max(job.getLong(org.apache.hadoop.mapreduce.lib.input.      FileInputFormat.SPLIT_MINSIZE, 1), minSplitSize);    // generate splits    ArrayList
splits = new ArrayList
(numSplits); NetworkTopology clusterMap = new NetworkTopology(); for (FileStatus file: files) { Path path = file.getPath(); long length = file.getLen(); if (length != 0) { FileSystem fs = path.getFileSystem(job); BlockLocation[] blkLocations; if (file instanceof LocatedFileStatus) { blkLocations = ((LocatedFileStatus) file).getBlockLocations(); } else { blkLocations = fs.getFileBlockLocations(file, 0, length); } if (isSplitable(fs, path)) { long blockSize = file.getBlockSize(); long splitSize = computeSplitSize(goalSize, minSize, blockSize); long bytesRemaining = length; while (((double) bytesRemaining)/splitSize > SPLIT_SLOP) { String[][] splitHosts = getSplitHostsAndCachedHosts(blkLocations, length-bytesRemaining, splitSize, clusterMap); splits.add(makeSplit(path, length-bytesRemaining, splitSize, splitHosts[0], splitHosts[1])); bytesRemaining -= splitSize; } if (bytesRemaining != 0) { String[][] splitHosts = getSplitHostsAndCachedHosts(blkLocations, length - bytesRemaining, bytesRemaining, clusterMap); splits.add(makeSplit(path, length - bytesRemaining, bytesRemaining, splitHosts[0], splitHosts[1])); } } else { String[][] splitHosts = getSplitHostsAndCachedHosts(blkLocations,0,length,clusterMap); splits.add(makeSplit(path, 0, length, splitHosts[0], splitHosts[1])); } } else { //Create empty hosts array for zero length files splits.add(makeSplit(path, 0, length, new String[0])); } } sw.stop(); if (LOG.isDebugEnabled()) { LOG.debug("Total # of splits generated by getSplits: " + splits.size() + ", TimeTaken: " + sw.elapsedMillis()); } return splits.toArray(new FileSplit[splits.size()]); } protected long computeSplitSize(long goalSize, long minSize, long blockSize) { return Math.max(minSize, Math.min(goalSize, blockSize)); }
这里对以上代码的划分策略进行整理:

  1. 遍历当前作业的所有输入文件,然后将累积这些文件的字节数并保存到变量totalSize中;
  2. 如果用户指定了mapreduce.job.maps参数,那么这个参数会被保存在入参numSplits中;
  3. 用户想要通过numSplits控制map任务的数量,那么需求对totalSize进行平分,以便确定每个map任务划分的输入大小。这个计算很简单,即使用totalSize除以numSplits,最后得到的目标划分大小存储在变量goalSize中;
  4. 常量SPLIT_MINSIZE实际是由参数mapreduce.input.fileinputformat.split.minsize来控制的,如果没有配置则默认是1。minSplitSize默认是1,切旧版FileIntputFormat没有设置此变量的地方。最后取SPLIT_MINSIZE和minSplitSize的最大值,并保存在变量minSize中;
  5. 遍历当前作业的每个输入文件,计算每个输入文件,将被划分的任务数量,最后将每个文件划分的任务数量合并起来就是整个作业划分的任务数量。
以上只是总体分析了作业的任务划分,有关每个输入文件的任务数量划分步骤如下:
  1. 判断文件的大小,只有文件字节数大于0才是有意义的;
  2. 判断文件是否是可以切分的,只有能够切分的文件才会继续进行任务数量划分;
  3. 调用文件的getBlockSize方法,获取文件的块大小并存储在变量blockSize中;
  4. 调用computeSplitSize方法计算最后划分给每个任务的输入大小,并保存在splitSize中。计算公式为:splitSize = max(minSize, min(goalSize, blockSize));
  5. 将文件按照splitSize的大小进行划分,不足splitSize大小的也算作一个任务划分数。

总结

根据以上分析发现旧版mapreduce和新版mapreduce的FileIntputFormat关于map任务数量划分的实现逻辑不同,在对它们进行开发和性能优化时要特别注意。

后记:个人总结整理的《深入理解Spark:核心思想与源码分析》一书现在已经正式出版上市,目前京东、当当、天猫等网站均有销售,欢迎感兴趣的同学购买。

京东: 

当当: 

你可能感兴趣的文章
林锐博士谈考研
查看>>
Vant Weapp小程序蹲坑之使用checkbox组件
查看>>
重载operator<<运算符时第二个参数最好不要写成指向对象的指针
查看>>
在ubuntu上编译 wpa_supplicant-2.6
查看>>
68ES6_解构_数组操作_对象操作
查看>>
poj——1470 Closest Common Ancestors
查看>>
Mysql Master/Slave Set Up
查看>>
自动化部署Newton版OpenStack (一)
查看>>
我的友情链接
查看>>
几个经典的Spring学习资料
查看>>
Objective-C 常用代码
查看>>
linux下IPTABLES配置详解
查看>>
由网络引起的打印故障和邮件问题
查看>>
xml相关
查看>>
如何将App从一个账号迁移到另一个账号?
查看>>
linux系统修改字符集
查看>>
phantomjs-截图比例
查看>>
javascript for of
查看>>
EF6 秘籍 2th:实体数据建模基础 (十二)使用条件过滤对象集合
查看>>
30天了解30种技术系列---(1)现代web应用服务器-Express.js
查看>>