MapReduce 任务提交加速

在日常工作中,我们经常遇到一些用户反馈任务提交特别慢,有些任务从运行启动脚本到任务真正提交到 RM 上,需要十几分钟有的甚至需要一个小时以上。分析这些任务我们很容易就发现,这些任务基本上都是因为依赖的本地资源(files, libjars, archives)文件过多或者输入数据过多(split input巨慢)导致的,因此本篇文章主要就是简单分析一下 MR 任务提交过程,然后总结一下加快提交速度的方法。

MR 任务提交过程

MRv2 中,任务的提交过程的代码调用路径大致是
org.apache.hadoop.mapreduce.Job#submit --> org.apache.hadoop.mapreduce.JobSubmitter#submitJobInternal --> org.apache.hadoop.mapred.YARNRunner#submitJob.

代码的逻辑也比较简单,主要做了以下几件事情:

  1. 创建 submitDir,上传依赖资源 (files, libjars, archives):
    这部分主要负责将任务在集群上运行时需要的资源文件上传到任务在 HDFS 上的 submitDir,以便于任务在 NM 上启动时,可以从 submitDir 将依赖资源本地化到 NM 上的任务的本地目录中,从而使任务运行时直接可以使用这些依赖资源。

    因为涉及到写入数据到 HDFS,所以当任务有许多的依赖资源需要上传时,该过程就会变得十分耗时。比如一些机器学习任务,会依赖特定版本的 python,以及一堆的 models 或者框架,上传过程需要将所有的依赖的文件都上传到 summitDir,因此耗时巨长。

  2. 输入数据分片(input split):
    这部分主要是遍历任务的输入目录,将所有输入数据文件按照分片规则进行分片操作,分片完成后将分片结果写入 submitDir,然后根据分片数量确定 map 数量。

    因为涉及到对于 HDFS 输入路径的遍历访问,所以当输入数据很多时,该过程也会变得非常耗时。

  3. 提交任务到 RM:
    这部分工作就比较简单,主要就是根据客户端配置生成任务提交请求,然后向 RM 发起任务提交。这部分工作比较简单,一般不存在比较耗时的状况。

提交任务加速

根据任务提交流程分析,我们知道了任务提交慢的原因主要是依赖资源过多或者输入数据过多导致。针对这两种情况,我们可以给出对应的解决方法。

依赖资源过多

对于依赖资源过多的问题,优化方法主要有三种。

  1. 本地依赖资源统一压缩打包后上传
    因为依赖资源文件上传 HDFS 是单个文件逐一上传的,依赖文件过多时,就会有许多次文件新建、写入关闭、过程,其中文件的新建和关闭耗费的时间可能比实际数据写入耗时还多。因此,我们可以考虑将所有依赖资源使用 tar 命令压缩打包成一个 tar 包,使用 -archives source.tar.gz 进行上传,从而达到减少文件创建过程,只有文件上传的耗时。

    这种方式会降低一大部分耗时,但是也会带来文件打包的时间消耗,总体耗时降低可能并不太明显。并且NM 本地化时会将 tar 包自动解压成为目录,需要将代码里所有将依赖路径改成 source/path/xxx 这种。因此这种方式只适合任务的依赖资源每次都会变化的任务,对于依赖资源基本不变的任务,可以考虑使用方法2.

  1. 依赖资源预先上传到 HDFS
    查看资源上传代码我们发现,任务上传依赖资源时,会检测依赖资源路径是不是 HDFS 路径,如果是就不再上传该资源。所以当我们预先把所有依赖文件预先上传到 HDFS 指定目录,然后在指定依赖文件时直接使用 HDFS 路径 (-archives hdfs://ns1/tmp/archives)时,任务提交时就会跳过该资源上传流程。

    这种方式可以说是一次上传,永远使用。后续任务提交时依赖资源上传流程几乎不存在耗时,能够大大减少依赖资源巨多的任务的提交时间。该方式适合依赖资源基本不变的任务,依赖资源上传后不需要修改。

  2. 使用 yarn shared cache
    yarn shared cache 是社区在 2.9 版本后提出的新功能,主要思想是使用单独的服务来托管所有集群任务的依赖资源。对于相同的依赖资源,直接从 shared cache 获取,不需要每次都上传。具体可参见 YARN Shared Cache 功能简介

输入数据过多

输入数据过多会导致任务提交过程中需要花费大量时间来遍历输入数据,然后对输入数据进行分片。

FileInputFormat 默认使用单线程遍历输入数据,但是提供了多线程处理功能,通过使用配置设置处理
线程数量即可。

1
mapreduce.input.fileinputformat.list-status.num-threads=10

经过实践,通过启用多线程处理功能,输入数据处理过程时间会成倍数的降低。

总结

如上述分析,MR 任务提交慢的的原因,要么是依赖资源过多导致上传耗时长,要么是输入数据过多导致输入分片耗时长。所以针对具体任务,我们分析下任务的具体状态,再根据上述的优化方法进行针对话优化即可。