HDFS PB级数据无感迁移实践

前言


前面文章笔者介绍了比较多关于HDFS RBF如何来解决多集群协调管理的问题,RBF里的Router服务在里面扮演着一个很重要的角色。它能让众多的NN对于client来说完全透明,鉴于这一点,我们可以很好地依托Router服务来做到数据的无感迁移,从而间接达到平衡各个namespace RPC load的效果。本文笔者将要阐述的是我们如何在RBF模式下做到数据的无感迁移。

数据无感迁移的挑战


关于大体量数据的迁移,笔者在前年做过一次PB级规模的数据迁移实践(HDFS千万级别文件数/PB规模量级的数据迁移实战总结),不过这次随着集群数据规模的进一步扩张,需要迁移的数据体量也更加庞大。倘若同样沿用之前的做法的话,对于用户停服的时间影响(数小时的服务影响时间)是无法接受的。因此在这次的数据迁移任务中,对于迁移过程的“无感”要求是最高的,如何做到对于用户接近无感的数据迁移,这是我们最大的一个目标,数据迁移的体量还是控制在PB级别。

数据无感迁移实践


在明确完数据迁移的要求之后,我们在调研实践一种新的无感迁移的方案,最终我们采用了社区Federation balance tool(HDFS-15294)的工具方案。这个工具底层依赖的同样是Hadoop自带的DistCp方案。另外,这个工具方案也能很好的支持RBF模式。

我们采用此方案的一个最大的原因是它实现了我们之前考虑过的一种基于初始拷贝+多循环增量拷贝的数据迁移方案。采用这种方式对于用户的影响时间会降低到一个很小的可以接受的时间值,因为用户只会在最后一次增量数据的拷贝过程里才会受到影响(不能读写目标数据)。Fed balance tool的增量拷贝基于的原理是利用了DistCp现有的支持snapshot diff的增量拷贝功能来做的。

虽然Fed balance tool在实现上已经做到将数据迁移的影响做到最小,但是我们在实际的迁移过程里还是踩了不少的坑。这里笔者来一一分享里面的一些经验总结,里面大部分是fed balance job里面触发的DistCp的一些问题,以及还有一些别的HDFS的问题。

数据迁移的踩坑和填坑


问题1:DistCp build copy list阶段OutOfMemory问题


我们知道DistCp任务在拷贝数据前,会进行一个copy list build的过程,这个过程中它会启动线程向source NameNode查询需要拷贝的数据目录,然后把这些信息写出到一个.seq的文件里面。但是我们在实际任务的运行里,发现这个过程经常会出现内存不够导致OutOfMemory的情况,甚至于我们已经将DistCp的heap调大到几十GB这么一个很大的量级了,问题还是没有解决。

后来通过打了heap dump后,发现是DistCp这边写出.seq文件的速度比实际查询source NN的速度慢导致里面的worker队列堆积,从而导致内存被用光。因为这个.seq结果文件是写在HDFS上面的,偶尔会因为集群有慢DN导致写出变慢。后来我们在队列层面做了size的限制,如果本身queue size大于给定的一个阈值后,进行sleep等待,避免output queue里面积压大量的待写出的文件信息结果。

问题2:DistCp的map task failure


我们迁移的数据量级非常大,因此指定了数千个map task去做拷贝,这里面经常会发现有些map task因为所在机器不稳定导致的上面的task 失败的情况,在经历了默认4次的task重试后,这个map task就会被标记为彻底failed的task。但是DistCp的job默认是不允许容忍失败任务的情况(为了保证数据拷贝的准确性),因此整个Job就失败了。但是这里Job重新启动拷贝的成本代价非常高,因为一个Job往往要拷贝几十小时的时间,如果在后期就因为一失败的task导致整个任务失败,影响太大了。

对于这个问题,我们想到了2种办法:第一种,采用上次迁移用过的办法,让DistCp去ignore map的failure错误,但是需要我们手动去补拷失败的文件。这种办法在只有one-time拷贝的过程里是可以行的通的,但是在我们目前会存在连续多轮增量拷贝的过程里面会有很大的问题。我们不可能每次失败都人工介入去拷贝失败的文件。因此在这里我们采用了另外一种增大map attempt重试(mapreduce.map.maxattempts)的方法,我们设置了一个很大的map几乎不可能会达到此重试次数的一个值,直到这个map任务最终能够成功跑完。每次map task重新跑的时候,之前的数据并不需要重新拷贝,这样的话,每次重试需要拷贝的数据的量并没有变多。

问题3:DistCp长尾任务


DistCp长尾任务问题在上次我们做迁移的时候也遇到过了,长尾任务的本质原因在于待拷贝的数据存在大小文件size参差不齐的情况。从数据拷贝的cost来分析,拷贝多个小文件的cost其实要比拷贝同等size(多个小文件总和size)的大文件要大不少。这就会导致拷贝大文件的map task往往结束的时间要比拷贝诸多小文件的时间要早。因此在这里我们采用的办法和之前类似,通过在切分文件时虚拟地增加文件 block,目录的size来平衡这里面的cost差异,具体来说,我们将小于一个标准block size的文件算成标准256MB的size,目前则当成是1/8的block size。后来通过这种方式,长尾任务的情况得到了很大的改善。

问题4:Fed balance job带宽过小问题


长尾任务解决好之后,我们时不时发现拷贝大文件的时候,map task总是跑的偏慢,后来发现是fed balance job默认带宽设置调小的问题。在DistCp里,给定的map task拷贝的默认带宽是100MB,但是Fed balance tool里面只给了10MB,然后fed balance tool job会用默认的10MB设置到DistCp job里面来使用。

    /* Specify bandwidth per map in MB. */
    private int bandwidth = 10;

问题5:数据最终拷贝的偏差问题


在解决完前面3个主要问题后,我们的Fed balance job终于顺顺利利地跑起来了,而且最终跑过了init copy的阶段(首轮初始拷贝),然后紧接着进入了后续的多轮增量copy的阶段。但增量数据其实量不多的,这个阶段很块也跑完了。正当我们心情激动万分地等待着数据迁移的结果验证的时候,错误还是发生了。为了增强数据的准确性验证,我们在社区fed balance tool代码版本里面额外增加了source target的count数的验证,里面包括文件数,目录数以及总size大小。假设我们在最后这步验证能过的话,基本上这个数据拷贝过程是完美的。上面提到的错误就是在这个验证过程里面出现了问题。

这个count数不匹配的文件只是代表了结果,至于背后的原因实际上非常的隐蔽。在经过数据拷贝这么多轮后,我们并不知道数据拷贝在哪一轮的时候出了问题,以及我们也不知道哪些文件拷贝的是有问题的(整个文件目录树涉及上千万个文件,十分不好找)。不过在对比sourcce和target count结果后,我们发现了一个关键的线索,每次都是size数不一致,目标的size数总是比源source小一些,但是文件和目录数都是匹配的。这说明target数据在拷贝的时候还是少拷贝了一点source的数据。

于是我们重新分析了fed balance tool里面的拷贝过程,我们初步把目标怀疑点指向了最后增量拷贝的阶段,因为在最后增量拷贝过程里面,它会进行最后数据的拷贝。在这个过程里面,也会进行一个关键的disable write的操作,一种很有可能发生的情况是open文件没彻底关掉,导致source端的数据还在写。然后这部分漏掉的数据没被DistCp任务同步到target那边。于是我们对这部分代码做了debug分析,确认这边在最后final copy前的确是关掉了open的file了,这时候是不会发生后续的写数据操作了。至此,我们也是陷入了一个死胡同,也没找到什么思路。但是我们的一个大方向是确定的,就是这些open file的拷贝可能存在着一些问题。

后面我们尝试在测试集群来人工模拟open file拷贝情况,我们在close open file操作的前后,创建snapshot,然后对比下这里面数据的差异,来观察到底DistCp的snapshot能不能同步里面的数据差异。在测试过程中,我们发现了一个关键的信息,open file在snapshot中并没有记录snapshot时刻的size,在最后关闭完open file后,这些snapshot拥有同样size的结果,比如下面这样的文件:

[hdfs@NN]$ /apache/hadoop/bin/hdfs dfs -ls /tmp/copy-test/.snapshot/snapshot0/
Found 1 items
-rw--w-r--   3 hdfs hadoop  805306380 2022-06-05 19:22 /tmp/copy-test/.snapshot/snapshot0/test.txt
[hdfs@NN]$ /apache/hadoop/bin/hdfs dfs -ls /tmp/copy-test/.snapshot/snapshot1/
Found 1 items
-rw--w-r--   3 hdfs hadoop  805306380 2022-06-05 19:22 /tmp/copy-test/.snapshot/snapshot1/test.txt

这显然不是我们原本预期的snapshot的效果,因为snapshot size都一样直接带来的后果是snapshot diff的无差别。这最终导致了open file后续写入的数据无法同步,这就解释了为什么最后final copy阶段总size数总存在偏差的问题。简单归结起来一句话:Snapshot认为open file在倒数第二次snapshot的时候和最终close open file时刻的最终snapshot里面的size是无差别的,不管这期间这个open file被close前写了多少的数据。

后来知道了这个原因后,我们在社区里找到了社区相关的JIRA(HDFS-11402:HDFS Snapshots should capture point-in-time copies of OPEN files),后来进行了及时的backport,最终解决了这个难题。最后,我们真正做到了PB级别数据分毫不差地迁移完成,只是在最后final copy阶段存在短短几分钟的服务影响时间。

相关资料


[1].https://issues.apache.org/jira/browse/HDFS-11402
[2].https://issues.apache.org/jira/browse/HDFS-15294

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:编程工作室 设计师:CSDN官方博客 返回首页
评论 3

打赏作者

Android路上的人

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值