DistCp工具简介
DistCp是Apache Hadoop提供的一个用于在分布式环境下高效复制大量数据的工具。DistCp工具可以在不同的Hadoop集群之间复制数据,也可以在同一集群中不同的HDFS目录之间复制数据。
distcp的源代码在Hadoop源代码树的hadoop-tools/hadoop-distcp目录下,其中最主要的类是org.apache.hadoop.tools.DistCp。DistCp类的实例是distcp工具的入口,它提供了多种用于配置复制任务的方法,如设置源目录、目标目录、带宽限制、忽略文件列表等。
官网介绍:https://hadoop.apache.org/docs/r3.3.3/hadoop-distcp/DistCp.html
参数详解
标识 |
描述 |
备注 |
-bandwidth <arg> |
指定每个map的带宽 |
以MB为单位,每个map任务都会被限制只能使用特定的带宽。这个数值也不是绝对的。map任务在复制的过程中会逐渐限制自身带宽的使用量,整个网络的带宽使用会逐渐趋向设定的值。 |
-m <num_maps> |
同时拷贝的最大数目 |
指定了拷贝数据时map的数目。请注意并不是map数越多吞吐量越大。 |
-skipcrccheck |
是否跳过源和目标之间的crc校验 |
一般跳过,不然复制很慢。但是跳过会存在数据安全性降低的情况。 |
-numListstatusThreads <arg> |
用于构建文件清单的线程数(最多40个) |
当文件目录结构复杂时应该适当增大该值。一般来说,对于文件数量在 10 万个以下的情况,可以将 -numListstatusThreads 参数设置为默认值 10 或稍微增加一些,比如设置为 20。如果文件数量较大,可以根据实际情况适当增加该参数的值。 |
-useiterator |
使用单线程 listStatusIterator 来构建列表 |
对于在客户端节省内存很有用。使用此选项将忽略 numListstatusThreads 选项 |
-strategy {dynamic|uniformsize} |
选择DistCp的复制策略 |
默认情况下,使用uniforsize,每个map复制的文件总大小均衡;可以设置为dynamic,使更快的map复制更多的文件,以提高性能 |
-p[rbugpcaxt] |
r: replication(副本数默认3) |
当使用-update选项时,只有当文件大小不同时才会同步文件状态。如果指定了-pa选项,DistCp还是会保留权限,因为ACLs是权限的超集 |
-i |
忽略失败 |
这个选项会比默认情况提供关于拷贝的更精确的统计, 同时它还将保留失败拷贝操作的日志,这些日志信息可以用于调试。最后,如果一个map失败了,但并没完成所有分块任务的尝试,这不会导致整个作业的失败。 |
-overwrite |
覆盖目标 |
如果一个map失败并且没有使用-i选项,不仅仅那些拷贝失败的文件,这个分块任务中的所有文件都会被重新拷贝。 它会改变生成目标路径的语义,所以 用户要小心使用这个选项。 |
-update |
如果源和目标的大小、块大小或校验和不同,则覆盖 |
执行覆盖的唯一标准是源文件和目标文件大小是否相同;如果不同,则源文件替换目标文件。 它也改变生成目标路径的语义, 用户使用要小心。 |
-append |
对名称相同但长度不同的文件进行增量拷贝。 |
如果源文件的长度比目标文件长,就比较公共部分的校验和。如果校验和相等,就使用读和追加的方式拷贝不同的部分。-append选型仅和-update一起使用,没有-skipcrccheck。 |
-delete |
从目标中删除(要和-update/-overwrite一起使用) |
会使得源文件丢失,改变生成目标路径的语义,用户使用要小心。 |
-atomic {-tmp <tmp_dir>} |
在指定的路径下进行原子提交 |
-atomic指定DistCp将元数据拷贝到临时目标位置,然后将数据整体原子式的提交到目标位置。结果就是数据要么整体都在目标位置,要么都不在。可选参数-tmp可以指定临时文件的位置。如果没有指定,就按默认值来。需要注意的是:tmp_dir必须在目标集群上。 |
-diff |
使用快照差异报告比较源和目标之间的差异 |
此选项仅与 -update 选项一起有效,并且应满足以下条件。 源文件系统和目标文件系统都必须是 DistributedFileSystem。 源 FS 上创建了两个快照 <oldSnapshot> 和 <newSnapshot>,并且 <oldSnapshot> 比 <newSnapshot> 旧。 目标具有相同的快照 <oldSnapshot>。自 <oldSnapshot> 创建以来,目标没有发生任何变化,因此 <oldSnapshot> 与目标的当前状态具有相同的内容。目标中的所有文件/目录与源的 <oldSnapshot> 相同。 |
-rdiff <newSnapshot> <oldSnapshot> |
使用给定两个快照之间的快照差异报告来识别自从在目标上创建快照 <oldSnapshot> 以来目标上发生的更改,并将差异反向应用于目标,并从源的 <oldSnapshot> 复制修改后的文件,以使目标与 <oldSnapshot> 相同。 |
此选项仅与 -update 选项一起有效,并且应满足以下条件。 源文件系统和目标文件系统都必须是 DistributedFileSystem。源和目标可以是两个不同的集群/路径,也可以是完全相同的集群/路径。在后一种情况下,修改后的文件从目标的 <oldSnapshot> 复制到目标的当前状态)。 目标 FS 上创建了两个快照 <newSnapshot> 和 <oldSnapshot>,并且 <oldSnapshot> 比 <newSnapshot> 旧。自从在目标上创建 <newSnapshot> 以来,没有对目标进行任何更改。 源具有相同的快照 <oldSnapshot>,其内容与目标上的 <oldSnapshot> 相同。目标的 <oldSnapshot> 中的所有文件/目录与源的 <oldSnapshot> 相同。 |
-blocksperchunk <blocksperchunk> |
每个块的块数。指定时,将文件拆分为块以并行复制 |
如果设置为正值,块数超过此值的文件将被拆分成块<blocksperchunk>块以并行传输,并在目的地重新组装。默认情况下,<blocksperchunk>为0,文件将完整传输,不分割。该开关仅在源文件系统实现getBlockLocations方法且目标文件系统实现concat方法时适用。 |
-log <logdir> |
记录日志到 <logdir> |
DistCp为每个文件的每次尝试拷贝操作都记录日志,并把日志作为map的输出。 如果一个map失败了,当重新执行时这个日志不会被保留。 |
-f <urilist_uri> |
使用<urilist_uri> 作为源文件列表 |
这等价于把所有文件名列在命令行中。urilist_uri列表应该是完整合法的URI。 |
-filters |
文件路劲更包含一系列模式字符串,一个字符串一行,与该模式匹配的文件路径将从复制过程中排除 |
支持由java.tuil.regex.Pattern指定的正则表达式。 |
-async |
异步运行DistCp,启动hadoop的Job之后立刻退出 |
会打印Hadoop的Job-id,方便追踪 |
-v |
在 SKIP/COPY 日志中记录附加信息(路径、大小) |
和-log一起用 |
-filelimit <n> |
限制文件总数 <= n |
弃用!在新的 DistCp 中被忽略 |
-sizelimit <n> |
限制文件总大小为 <= n 字节 |
弃用!在新的 DistCp 中被忽略 |
-copybuffersize |
要使用的复制缓冲区的大小。默认情况下,<copybuffersize> 设置为 8192B |
|
-xtrack <path> |
将有关丢失的源文件的信息保存到指定路径。 |
此选项仅与 -update 选项一起有效。这是一个实验属性,不能与 -atomic 选项一起使用。 |
-direct |
直接写入目标路径 |
当目标是对象存储时,对于避免可能非常昂贵的临时文件重命名操作很有用。一般不建议使用。一般实际传输都是先创建临时文件,最后mv重命名一下即可。 |
调优FAQs
1. map数更多为什么DistCp任务没有更快?
目前,DistCp 的最小工作单位是文件(a file),一个文件仅由一个map处理。将map数量增加到超过文件数量的值不会产生任何性能优势,启动的map数量将等于文件数量。
2. 为什么DistCp内存不足?
如果从源路径复制的文件/目录的数量非常大(比如1,000,000 个路径),DistCp 在确定复制路径列表时可能会用完内存。解决方法可通过更改JVM堆大小参数-Xmx,如:
-Dmapreduce.map.java.opts='-Xmx768m' -Dmapreduce.map.memory.mb=1024
3. MapReduce存在的副作用
如果一个Map失败复制了它的一个输入,将会产生多种副作用:
- 除非指定 -overwrite ,否则由先前map任务成功复制的文件将在重新执行时标记为“跳过”(skip)。
- 如果 map 失败 mapreduce.map.maxattempts 次,剩余的 map 任务将被终止(除非设置了 -i)。
- 如果 mapreduce.map.speculative 设置为 final 和 true,则复制的结果是未定义的。
4. 任务失败,map task报“DFS Read: java.io.IOException: Could not obtain block”
这是由于“_distcp_src_files”这个文件的备份数是系统默认值,例如hadoop-site.xml里面设置了dfs.replication=3,那么_distcp_src_files文件的备份数则创建之后就为3了。当map数非常多,以至于超过了_distcp_src_files文件三个副本所在datanode最大容纳上限的时候,部分map task就会出现获取不了block的问题。对于DistCp来说“-i”参数一般是绝对不能使用的,因为设置了该参数,这个问题就会被掩盖,带来的后果就是拷贝完缺失了部分数据。比较好的做法是在计算了总map数之后,自动增加_distcp_src_files这个文件的备份数,这样一来访问容纳上限也会跟着提高,上述问题就不会再出现了。当前社区已对此有了简单fix,直接将备份数设置成了一个较高的数值。一般说来对于计算资源有限的集群来说,过多的maptask并不会提高拷贝的效率,因此我们可以通过-m参数来设定合理的map数量。一般说来通过观察ganglia,bytes_in、bytes_out达到上限就可以了。
5. Owner同步问题
DistCp工具的提示信息非常少,对于海量数据来说,DistCp初始阶段准备拷贝文件列表和结束阶段设定Owner同步耗时都比较长,但却没有任何提示信息。这是一个很奇怪的地方,拷贝过程中,mapred会打印进度信息到客户端,这时候可以看到百分比,等结束的时候可以看到过程中的一些统计信息。如果你设置了-p参数,此时就会处于一个停滞的状态,没有任何输出了。由于Owner同步没有在map task里面去做,放在客户端就必然成为一个单线程的工作,耗时也会比较长。我以前犯过的错误就是启动distcp后看jobtracker页面出现作业了,就kill了客户端的进程,这样一来就导致Owner不会同步。现在做法都是用“nohup nice -n 0”把进程放到后台让其自动结束。
6. 长尾问题
DistCp切分map的时候,充分考虑了每个map需要拷贝的数据量,尽量保持平均,但是却完全没有考虑碎文件和整块文件拷贝耗时不同的问题。此外,某些task所在tasktracker机器由于故障之类原因也会导致性能较差,拖慢了整体节奏。拷贝大量数据的时候总会因为这些原因出现长尾。通过在InputSplit的时候同时考虑数据量和文件个数的均衡可以解决碎文件和整文件拷贝耗时不同的问题。而部分task运行慢的问题,目前看起来则没有很好的解决方案。
7. 大文件传输超时
在执行DistCp命令时,如果复制的部分文件内容较大,建议修改执行复制任务的MapReduce的超时时间。可以通过在 DistCp 命令中指定 mapreduce.task.timeout 来实现。
hadoop distcp -Dmapreduce.task.timeout=1800000 hdfs://cluster1/source hdfs://cluster2/target
或过滤掉大文件
hadoop distcp -filters /opt/client/filterfile hdfs://cluster1/source hdfs://cluster2/target
8. 客户端内存不足退出“java.lang.OutOfMemoryError”
如果 DistCp 命令意外退出,则会显示错误消息“java.lang.OutOfMemoryError”。 这是因为运行复制命令所需的内存超过了预设的内存限制(默认值:128 MB)。可以通过修改<安装路径>/HDFS/component_env中的CLIENT_GC_OPTS来改变客户端的内存上限。例如要设置内存上限为1GB,参考如下配置:
CLIENT_GC_OPTS="-Xmx1G"
修改生效
source {Client installation path}/bigdata_env
在其他解决案例中,也有通过命令行解决该问题的方法
export HADOOP_CLIENT_OPTS="-Xms64m -Xmx1024m"
9. 采用dynamic策略报错"Too many chunks created with splitRatio"
使用动态策略运行DistCp命令时,命令异常退出,并报错"Too many chunks created with splitRatio"
原因:distcp.dynamic.max.chunks.tolerable(默认值:20000)的值小于distcp.dynamic.split.ratio(默认值:2)的值乘以Map的数量.当 Maps 的数量超过 10,000 个时,就会出现此问题。您可以使用 -m 参数将 Maps 数量减少到 10,000 个以下,或使用-D参数将distcp.dynamic.max.chunks.tolerable设置为较大的值,如
hadoop distcp -Ddistcp.dynamic.max.chunks.tolerable=30000 -strategy dynamic hdfs://cluster1/source hdfs://cluster2/target
参考文献:
https://hadoop.apache.org/docs/r3.3.3/hadoop-distcp/DistCp.html
https://docs.cloudera.com/HDPDocuments/HDP3/HDP-3.1.5/administration/content/distcp_faq.html
https://blogjava.net/qileilove/archive/2013/11/04/405967.html
https://support.huaweicloud.com/intl/en-us/cmpntguide-mrs/mrs_01_0794.html