mapTask并行度的决定机制

  一个job的map阶段并行度由客户端在提交job时决定,而客户端对map阶段并行度的规划的基本逻辑为:将待处理数据执行逻辑切片(即按照一个特定切片大小,将待处理数据划分成逻辑上的多个split),然后每一个split分配一个mapTask并行实例处理。

FileInputFormat切片机制

1、默认切片定义在InputFormat类中的getSplit()方法

2、FileInputFormat中默认的切片机制:

a) 简单地按照文件的内容长度进行切片

b) 切片大小,默认等于hdfs的block大小

c) 切片时不考虑数据集整体,而是逐个针对每一个文件单独切片

比如待处理数据有两个文件:

file1.txt    260M
file2.txt 10M

经过FileInputFormat的切片机制运算后,形成的切片信息如下:

file1.txt.split1--  0~128
file1.txt.split2-- 128~260 //如果剩余的文件长度/切片长度<=1.1则会将剩余文件的长度并未一个切片
file2.txt.split1-- 0~10M

3、FileInputFormat中切片的大小的参数配置

通过分析源码,在FileInputFormat中,计算切片大小的逻辑:Math.max(minSize, Math.min(maxSize, blockSize)); 切片主要由这几个值来运算决定。

minsize:默认值:1
配置参数: mapreduce.input.fileinputformat.split.minsize maxsize:默认值:Long.MAXValue
配置参数:mapreduce.input.fileinputformat.split.maxsize blocksize:值为hdfs的对应文件的blocksize 配置读取目录下文件数量的线程数:public static final String LIST_STATUS_NUM_THREADS =
      "mapreduce.input.fileinputformat.list-status.num-threads";

因此,默认情况下,Math.max(minSize, Math.min(maxSize, blockSize));切片大小=blocksize

maxsize(切片最大值):参数如果调得比blocksize小,则会让切片变小。

minsize(切片最小值):参数调的比blockSize大,则可以让切片变得比blocksize还大。

选择并发数的影响因素:

1、运算节点的硬件配置

2、运算任务的类型:CPU密集型还是IO密集型

3、运算任务的数据量

3、hadoop2.6.4源码解析

org.apache.hadoop.mapreduce.JobSubmitter类

   //得到job的map任务的并行数量
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()) {
maps = writeNewSplits(job, jobSubmitDir);
} else {
maps = writeOldSplits(jConf, jobSubmitDir);
}
return maps;
} @SuppressWarnings("unchecked")
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<InputSplit> splits = input.getSplits(job);
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);
return array.length;
}

切片计算逻辑,关注红色字体代码即可。

public List<InputSplit> getSplits(JobContext job) throws IOException {
Stopwatch sw = new Stopwatch().start();
long minSize = Math.max(getFormatMinSplitSize(), getMinSplitSize(job));
long maxSize = getMaxSplitSize(job);
// generate splits
List<InputSplit> splits = new ArrayList<InputSplit>();
List<FileStatus> files = listStatus(job);
   //遍历文件,对每一个文件进行如下处理:获得文件的blocksize,获取文件的长度,得到切片信息(spilt 文件路径,切片编号,偏移量范围)
for (FileStatus file: files) {
Path path = file.getPath();
long length = file.getLen();
if (length != 0) {
BlockLocation[] blkLocations;
if (file instanceof LocatedFileStatus) {
blkLocations = ((LocatedFileStatus) file).getBlockLocations();
} else {
FileSystem fs = path.getFileSystem(job.getConfiguration());
blkLocations = fs.getFileBlockLocations(file, 0, length);
}
if (isSplitable(job, path)) {
long blockSize = file.getBlockSize();
long splitSize = computeSplitSize(blockSize, minSize, maxSize);
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;
} if (bytesRemaining != 0) {
int blkIndex = getBlockIndex(blkLocations, length-bytesRemaining);
splits.add(makeSplit(path, length-bytesRemaining, bytesRemaining,
blkLocations[blkIndex].getHosts(),
blkLocations[blkIndex].getCachedHosts()));
}
} else { // not splitable
splits.add(makeSplit(path, 0, length, blkLocations[0].getHosts(),
blkLocations[0].getCachedHosts()));
}
} else {
//Create empty hosts array for zero length files
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());
sw.stop();
if (LOG.isDebugEnabled()) {
LOG.debug("Total # of splits generated by getSplits: " + splits.size()
+ ", TimeTaken: " + sw.elapsedMillis());
}
return splits;
}
 public static final String SPLIT_MINSIZE =
"mapreduce.input.fileinputformat.split.minsize"; public static final String SPLIT_MAXSIZE =
"mapreduce.input.fileinputformat.split.maxsize"; long minSize = Math.max(getFormatMinSplitSize(), getMinSplitSize(job)); //保证切分的文件长度最小不得小于1字节
protected long getFormatMinSplitSize() {
return 1;
} //如果没有在conf中设置SPLIT_MINSIZE参数,则取默认值1字节。
public static long getMinSplitSize(JobContext job) {
return job.getConfiguration().getLong(SPLIT_MINSIZE, 1L);
} //得到切片文件的最大长度
long maxSize = getMaxSplitSize(job); //如果没有在conf中设置SPLIT_MAXSIZE参数,则去默认值Long.MAX_VALUE字节。
public static long getMaxSplitSize(JobContext context) {
return context.getConfiguration().getLong(SPLIT_MAXSIZE,
Long.MAX_VALUE);
} //读取指定目录下的所有文件的信息
List<FileStatus> files = listStatus(job);
//如果没有指定开启几个线程读取,则默认一个线程去读文件信息,因为存在目录下有上亿个文件的情况,所以有需要开启多个线程加快读取。
int numThreads = job.getConfiguration().getInt(LIST_STATUS_NUM_THREADS,
DEFAULT_LIST_STATUS_NUM_THREADS);
public static final String LIST_STATUS_NUM_THREADS =
"mapreduce.input.fileinputformat.list-status.num-threads";
public static final int DEFAULT_LIST_STATUS_NUM_THREADS = 1; //计算切片文件的逻辑大小
long splitSize = computeSplitSize(blockSize, minSize, maxSize);
protected long computeSplitSize(long blockSize, long minSize,
long maxSize) {
return Math.max(minSize, Math.min(maxSize, blockSize));
} private static final double SPLIT_SLOP = 1.1; // 10% slop
//判断剩余文件与切片大小的比是否为1.1.
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;
}

map并行度

  如果job的每个map或者reduce的task的运行时间都只有30-40秒钟(最好每个map的执行时间最少不低于一分钟),那么就减少该job的map或者reduce数。每一个task的启动和加入到调度器中进行调度,这个中间的过程可能都要花费几秒钟,所以如果每个task都非常快就跑完了,就会在task的开始和结束的时候浪费太多的时间。

  配置task的JVM重用可以改善该问题:
  (mapred.job.reuse.jvm.num.tasks,默认是1,表示一个JVM上最多可以顺序执行的task数目(属于同一个Job)是1。也就是说一个task启一个JVM)。

小文件的场景下,默认的切片机制会造成大量的maptask处理很少量的数据,效率低下:

解决方案:

  推荐:把小文件存入hdfs之前进行预处理,先合并为大文件后再上传。

  折中:写程序对hdfs上小文件进行合并再跑job处理。

  补救措施:如果大量的小文件已经存在hdfs上了,使用combineInputFormate组件,它可以将众多的小文件从逻辑上规划到一个切片中,这样多个小文件就可以交给一个maptask操作了。

MapReduce中map并行度优化及源码分析的更多相关文章

  1. mapTask并行度优化及源码分析

    mapTask并行度的决定机制 一个job的map阶段并行度由客户端在提交job时决定,而客户端对map阶段并行度的规划的基本逻辑为:将待处理数据执行逻辑切片(即按照一个特定切片大小,将待处理数据划分 ...

  2. RocketMQ中Broker的HA策略源码分析

    Broker的HA策略分为两部分①同步元数据②同步消息数据 同步元数据 在Slave启动时,会启动一个定时任务用来从master同步元数据 if (role == BrokerRole.SLAVE) ...

  3. Fabric2.2中的Raft共识模块源码分析

    引言 Hyperledger Fabric是当前比较流行的一种联盟链系统,它隶属于Linux基金会在2015年创建的超级账本项目且是这个项目最重要的一个子项目.目前,与Hyperledger的另外几个 ...

  4. 【Java】NIO中Selector的select方法源码分析

    该篇博客的有些内容和在之前介绍过了,在这里再次涉及到的就不详细说了,如果有不理解请看[Java]NIO中Channel的注册源码分析, [Java]NIO中Selector的创建源码分析 Select ...

  5. RocketMQ中Broker的刷盘源码分析

    上一篇博客的最后简单提了下CommitLog的刷盘  [RocketMQ中Broker的消息存储源码分析] (这篇博客和上一篇有很大的联系) Broker的CommitLog刷盘会启动一个线程,不停地 ...

  6. Flink中Idle停滞流机制(源码分析)

    前几天在社区群上,有人问了一个问题 既然上游最小水印会决定窗口触发,那如果我上游其中一条流突然没有了数据,我的窗口还会继续触发吗? 看到这个问题,我蒙了???? 对哈,因为我是选择上游所有流中水印最小 ...

  7. List中的ArrayList和LinkedList源码分析

    ​ List是在面试中经常会问的一点,在我们面试中知道的仅仅是List是单列集合Collection下的一个实现类, List的实现接口又有几个,一个是ArrayList,还有一个是LinkedLis ...

  8. 【Android笔记】Thread类中关于join()方法的源码分析

    1.join()方法的作用: 例如有一个线程对象为Thread1,在main()方法中调用Thread1.join()方法可使得当前线程(即主线程)阻塞,而执行Thread1线程. 2.源码分析(以上 ...

  9. [源码分析]Java1.8中StringJoiner的使用以及源码分析

    [源码分析]StringJoiner的使用以及源码分析 StringJoiner是Java里1.8新增的类, 或许有一部分人没有接触过. 所以本文将从使用例子入手, 分析StringJoiner的源码 ...

随机推荐

  1. java单元测试

    单元测试 1.简介 在日常开发中,我们编写的任何代码都需要经过严谨的测试才可以发布.以往的测试方法都是通过编写一个main函数进行简单的测试,并使用大量的print语句输出结果,这种方法其实是不可取的 ...

  2. c/c++ 重载new,delete运算符 placement new

    重载new,delete运算符 new,delete在c++中也被归为运算符,所以可以重载它们. new的行为: 先开辟内存空间 再调用类的构造函数 开辟内存空间的部分,可以被重载. delete的行 ...

  3. c/c++ const this指针

    const this指针 方法列表后面的const是什么含义呢?答案:不可以在方法里修改成员变量 class Test{ public: void fun()const{ //data = 10;// ...

  4. App分享之微信微博等各个社交平台的分享授权规则和常见问题

    一.新浪微博分享规则 新浪微博支持分享类型: 应用内分享也就是网页分享支持: 文字,文字+图片,要分享链接需要链接添加在text里分享 客户端分享支持:文字,图片,文字+图片,图片+文字+链接 参数说 ...

  5. MySQL服务使用

    MySQL服务使用 1. 启动服务 启动服务: service mysql start 或者 sudo /etc/init.d/mysql start 2. 关闭服务 关闭服务: service my ...

  6. Java循环语句怎么用?经典排序算法见真知

    Java中循环语句的使用,莫过于在排序算法中使用得最为经典. 排序算法非常的多,不过大体可以分为两种: 一种是比较排序,主要有:冒泡排序,选择排序,插入排序,归并排序,堆排序,快速排序等. 另一种是非 ...

  7. vue v-nav指令属性列表

    v-nav指令属性列表 属性名 属性描述 类型 必选 默认 title 导航栏标题 String No 空字符串 showBackButton 是否显示(左边的)返回按钮 Boolean No fal ...

  8. 设计模式のFlyweight(享元模式)----结构模式

    一.产生背景 享元模式:它使用共享物件,用来尽可能减少内存使用量以及分享资讯给尽可能多的相似物件:它适合用于只是因重复而导致使用无法令人接受的大量内存的大量物件.通常物件中的部分状态是可以分享.常见做 ...

  9. [matlab] 1.拟合

    x = [1 2 3 4 5 6 7 8 9 ]; y = [9 7 6 3 -1 2 5 7 20]; p=polyfit(x,y,3); %数字代表拟合函数的阶数 xi=0:0.01:10; yi ...

  10. Spring Boot+Spring Security+JWT 实现 RESTful Api 权限控制

    摘要:用spring-boot开发RESTful API非常的方便,在生产环境中,对发布的API增加授权保护是非常必要的.现在我们来看如何利用JWT技术为API增加授权保护,保证只有获得授权的用户才能 ...