MapReduce切片机制以及maptask和reducetask并行度设置

 

1 MapReduce运行流程

1最先启动MRAppMaster,MRAppMaster根据job的描述信息,计算需要的maptask实例的数量,然后向集群申请机器,启动相应数量的maptask进程。

2 maptask启动之后,根据给定的数据切片范围进行数据处理。

A利用指定的inputformat来获取RecordReader对象读取数据,形成KV输入。

B将输入的kv对传递给客户定义的map方法,做逻辑运算,将map方法输出的kv对收集到缓存。

C将缓存中的KV对按照K分区排序会溢写到磁盘文件。

3 MRAppMaster监控到所有maptask进程任务完成之后(真实情况是,某些maptask进程处理完成以后,就会开始启动reducetask去已经完成maptask处去fetch数据),会根据客户指定的参数启动相应数量的reducetask进程。并告知reducetask进程要处理的数据范围(数据分区)。

4 ReduceTask进程启动以后,根据MRAppMaster告知的待处理数据的位置,抓取maptask的输出结果文件,并在本地进行重新归并排序。按照相同key的KV为一个组,调用客户定义的reduce方法进行逻辑运算,收集输出的结果KV,调用客户指定的outputformat将结果数据输出到外部的存储。

2 maptask并行度决定机制

Maptask的并行度决定了map阶段的任务处理并发程度。

一个job的map阶段并行度由客户端提交的job决定。

客户端对map阶段并行度的规划逻辑为:

将待处理数据执行逻辑切片。按照一个特定切片的大小,将待处理的数据划分成逻辑上的多个split,然后每一个split分配一个maptask实例,并进行处理。

这段逻辑以及形成的切片规划描述文件,是由FileInputFormat实现类的getSplits方法来完成的。该方法返回List<InputSplit>,每个InputSplit封装了一个逻辑切片的信息,包括长度和位置等信息。

3 切片机制

FileInputFormat的getSplits方法。

public List<InputSplit> getSplits(JobContext job) throws IOException {
    Stopwatch sw = (new Stopwatch()).start();
    long minSize = Math.max(this.getFormatMinSplitSize(), getMinSplitSize(job));
    long maxSize = getMaxSplitSize(job);
    List<InputSplit> splits = new ArrayList();
    List<FileStatus> files = this.listStatus(job);
    Iterator i$ = files.iterator();

    while(true) {
        while(true) {
            while(i$.hasNext()) {
                FileStatus file = (FileStatus)i$.next();
                Path path = file.getPath();
                long length = file.getLen();
                if (length != 0L) {
                    BlockLocation[] blkLocations;
                    if (file instanceof LocatedFileStatus) {
                        blkLocations = ((LocatedFileStatus)file).getBlockLocations();
                    } else {
                        FileSystem fs = path.getFileSystem(job.getConfiguration());
                        blkLocations = fs.getFileBlockLocations(file, 0L, length);
                    }

                    if (this.isSplitable(job, path)) {
                        long blockSize = file.getBlockSize();
                        long splitSize = this.computeSplitSize(blockSize, minSize, maxSize);

                        long bytesRemaining;
                        int blkIndex;
                        for(bytesRemaining = length; (double)bytesRemaining / (double)splitSize > 1.1D; bytesRemaining -= splitSize) {
                            blkIndex = this.getBlockIndex(blkLocations, length - bytesRemaining);
                            splits.add(this.makeSplit(path, length - bytesRemaining, splitSize, blkLocations[blkIndex].getHosts(), blkLocations[blkIndex].getCachedHosts()));
                        }

                        if (bytesRemaining != 0L) {
                            blkIndex = this.getBlockIndex(blkLocations, length - bytesRemaining);
                            splits.add(this.makeSplit(path, length - bytesRemaining, bytesRemaining, blkLocations[blkIndex].getHosts(), blkLocations[blkIndex].getCachedHosts()));
                        }
                    } else {
                        splits.add(this.makeSplit(path, 0L, length, blkLocations[0].getHosts(), blkLocations[0].getCachedHosts()));
                    }
                } else {
                    splits.add(this.makeSplit(path, 0L, length, new String[0]));
                }
            }

            job.getConfiguration().setLong("mapreduce.input.fileinputformat.numinputfiles", (long)files.size());
            sw.stop();
            if (LOG.isDebugEnabled()) {
                LOG.debug("Total # of splits generated by getSplits: " + splits.size() + ", TimeTaken: " + sw.elapsedMillis());
            }

            return splits;
        }
    }
}

获取切片大小的方法。

long splitSize = this.computeSplitSize(blockSize, minSize, maxSize);
protected long computeSplitSize(long blockSize, long minSize, long maxSize) {
    return Math.max(minSize, Math.min(maxSize, blockSize));
}

computeSplitSize的计算逻辑是在maxSize和blockSize之间取一个最小值,然后将这个最小值和minSize比较,取结果较大的那个值。这个方法传进去了3个参数。blockSize、minSize、maxSize。

blockSize:默认值是128M,通过dfs.blocksize设置。

minSize:默认值是1,通过mapreduce.input.fileinputformat.split.minsize指定。

maxSize: 默认值是Long.MaxValue,可以通过mapreudce.input.fileinputformat.split.maxsize指定。

获得最小值的代码:

public static long getMinSplitSize(JobContext job) {
    return job.getConfiguration().getLong("mapreduce.input.fileinputformat.split.minsize", 1L);
}
long minSize = Math.max(this.getFormatMinSplitSize(), getMinSplitSize(job));


作比较取值的时候,会将job中设置的最小值和getFormatMinSplitSize得到的最小值作比较,得到较大的那个值,作为minSize。但是这个最小值,不一定按设定的来。在getSplits中还有一个限制。

protected long getFormatMinSplitSize() {
    return 1L;
}
public static long getMaxSplitSize(JobContext context) {
    return context.getConfiguration().getLong("mapreduce.input.fileinputformat.split.maxsize", 9223372036854775807L);
}

以上是获得最大值的代码。

4 Maptask并行度经验

1、如果job的每个map或者reduce的运行时间都只有30-40s,那么就减少这个job的map或者reduce的数量。因为计算本来就没有花费多少时间,大部分时间都浪费在了task的设置以及加入调度器中进行调度了。

配置task的jvm重用。

mapred.job.reuse.jvm.num.tasks,默认值为1,表示一个jvm上面最多可以顺序执行的属于同一个job的task数目是1。即一个task开启一个jvm。该值可以在mapred-site.xml文件中更改,配置成多个,意味着多个task运行了在同一个jvm上面,但不是同时执行,而是排队顺序执行。

2、如果iniput的文件非常大,如1TB,可以考虑将blocksize调大。比如256MB或者512MB。

5 reduceTask并行度决定机制

Maptask的并发数由切片决定,但是reducetask的数量可以直接手动设置。

比如:

job.setNumReduceTask(4);

如果数据分布不均匀,那么可能在reduce阶段产生数据倾斜。

Reducetask的数量不是任意设定的,要考虑业务的逻辑,在需要计算全局汇总结果的时候,就只能有1个reducetask。

尽量不要运行太多的reducetask。大多数job,最好的reduce个数最多和集群中的reduce持平,或者比集群的reduce slots小。这个对小集群而言,尤其重要。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值