HDFS-16420: HDFS EC 可能会丢数据!
1概述
最近 HDFS JIRA 关于 EC 纠删码有个值得关注的 bug:在删除冗余 striped blocks 时,可能会将同一机架上其他唯一的 data block 删除,从而造成数据丢失。
目前前社区通过 HDFS-16420 在 3.2.3、3.3.2、3.4.0 这三个版本中修复了该 bug:
该 bug 是在删除冗余 striped blocks 时触发,本文以 HDFS 3.3.1 为例,先分析该 bug 产生的原因,再讨论如何修复 bug。
2原因
目前 Striped Block 和 Contiguous Block 删除冗余数据调用的方法大致相同。为方便表述,下文用 Contiguous Block 和 replica 来说明删除冗余块的细节,对于 Striped Block 而言,将 replica 替换为 internal block 即可。
删除冗余块的时序图如下:
可以看到,每次筛选待删除的冗余 replica 时,都会更新剩余待删除 replica 所在的 DatanodeStorageInfo 列表信息,这部分更新逻辑在 adjustSetsWithChosenReplica 方法中实现,如下:
public void adjustSetsWithChosenReplica(
final Map<String, List<DatanodeStorageInfo>> rackMap,
final List<DatanodeStorageInfo> moreThanOne,
final List<DatanodeStorageInfo> exactlyOne,
final DatanodeStorageInfo cur) {
final String rack = getRack(cur.getDatanodeDescriptor());
final List<DatanodeStorageInfo> storages = rackMap.get(rack);
storages.remove(cur);
if (storages.isEmpty()) {
rackMap.remove(rack);
}
if (moreThanOne.remove(cur)) {
if (storages.size() == 1) {
final DatanodeStorageInfo remaining = storages.get(0);
moreThanOne.remove(remaining);
exactlyOne.add(remaining);
}
} else {
exactlyOne.remove(cur);
}
}
参数含义如下:
rackMap: 所有 replica 的 rack 到 DatanodeStorageInfo 列表的映射的哈希表
moreThanOne: 同一 rack 多于一个 replica 的 DatanodeStorageInfo 待删除候选列表
exactlyOne: 同一 rack 只有一个 replica 的 DatanodeStorageInfo 待删除候选列表
cur: 待删除 replica 所在的 DatanodeStorageInfo
在处理 Contiguous Block 多余的 replicas 时,其 rackMap 的DatanodeStorageInfo 列表等于如下两部分之和:
moreThanOne 中 DatanodeStorageInfo 列表
exactlyOne 中 DatanodeStorageInfo 列表
对于 Striped Block 而言,其一个 Block Group 有多个 internal block,这些 internal block 并不相同,而冗余的就是这些 internal block。其 rackMap 的 DatanodeStorageInfo 列表等于如下三部分之和:
moreThanOne 中 DatanodeStorageInfo 列表
exactlyOne 中 DatanodeStorageInfo 列表
不冗余 internal block 所在的 DatanodeStorageInfo 列表
以下图为例:
dataBlock1、dataBlock6 是待删除的冗余 internal block,则
moreThanOne:["dn7", "dn8", "dn10"]
exactlyOne: ["dn1", "dn12"]
rackMap: [{"/rack1": ["dn1"]}; {"/rack2": ["dn2"]}; ... {"rack9": ["dn12"]}]
回到前面的代码,在 moreThanOne 删除 DatanodeStorageInfo X 后,若 X 所在机架 R 只剩一个 DatanodeStorageInfo Y 时,则将 Y 从 moreThanOne 中删除,并添加到 exactlyOne 中。对于 Striped Block 类型而言,剩下的 Y 不一定属于 moreThanOne,其还可能是不冗余 internal block 所在的 DatanodeStorageInfo。当前实现的代码会将 Y 错误的加入到 exactlyOne 中,并在后续逻辑中删除,从而引起 internal block 丢失。
当然,在大多数情况下该 bug 并不会真的造成数据丢失,原因是当 NameNode 检查到 block 丢失时,会通过心跳向 DataNode 发送恢复 block 的指令,然后 DataNode 通过计算剩下的 data blocks 和 parity blocks 来恢复缺失的 block。
但如果短时间内同一 block group 中多块唯一的 blocks 被误删,则会导致 HDFS 无法通过剩余的 blocks 来恢复缺失的 blocks。以 RS-6-3 策略为例,若短时间内 4 块和 4 块以上 blocks 丢失,HDFS 将无法重新构建出缺失的 blocks,造成数据丢失。从 HDFS-16420 的讨论看,确实有部分用户的数据丢失。
知道了 bug 原因,那么修改该问题的思路就很简单了:若 moreThanOne 能成功删除 remaining,才将 remaining 加入到 exactlyOne 中,如 HDFS-16420 的 patch 所示:
if (moreThanOne.remove(remaining)) {
exactlyOne.add(remaining);
}
何时检测并删除冗余块
从源码的调用来看,如下几种情况会触发检测冗余块的流程:
新添加 block 到 blocksMap
文件最终写入时,在关闭文件后,会检查该文件相关的 block 是否冗余
从 decommission 或 maintenance 状态出来的 DataNode 重新上线
检测 block 异常的 replica 数,如 fsck 命令的 -replicate 参数
设置副本数
对于 EC 而言,主要是前三种情况触发冗余块的检测。
3受影响版本
如下版本会受到该 bug 影响:
除 hadoop 3.2.3、hadoop 3.3.2 和 hadoop 3.4.0 以下的 hadoop 3.x.x 系列版本
cdh 6.0.x、6.1.x、6.2.x、6.3.x
HDP 3.0.x、HDP 3.1.x
即便使用上述版本的大数据平台,若目录未配置 EC 策略,也不会受到影响。
命令
// 查看指定目录下哪些文件使用了 EC:
$ hdfs fsck /path -files | grep " erasure-coded:"
// 查看指定目录的策略:
$ hdfs ec -getPolicy -path /path
//取消指定目录的 EC 策略:
$ hdfs ec -unsetPolicy -path /path
4修复方案
若使用商业大数据平台,可咨询供应商如何修复;
若使用开源 Hadoop 方案,通过 git cherry-pick 87ea3b1418a34ed3 修复,重新编译并滚动发布。
其它 EC 相关的 JIRA
最近还有如下和 EC 相关且值得关注的 JIRA:
HDFS-16422:Fix thread safety of EC decoding during concurrent preads
HDFS-16333:fix balancer bug when transfer an EC block
HDFS-16268:Balancer stuck when moving striped blocks due to NP
从目前看,虽然一些一二线大厂在生产环境使用 HDFS EC,社区也在不断修复 EC 相关的 bug,但 HDFS EC 本身仍可能存在一些安全隐患,因此建议只对不太重要的冷数据设置 EC 策略,而这些数据要么能通过上游恢复,要么即使丢失也不对生产业务造成任何影响。