HDFS写文件时,如果在写完第一副本后第二副本写入失败,会发生什么?
在HDFS中写文件时,如果采用默认的3副本策略,数据是通过管道(Pipeline)的方式进行传输的(例如:Client -> DataNode 1 -> DataNode 2 -> DataNode 3)。
如果在写完第一副本(DataNode 1)后,写入第二副本(DataNode 2)时发生失败,整个写操作并不会立即失败。HDFS 会触发自动的容错机制,这个过程被称为管道恢复(Pipeline Recovery)。
以下是具体的发生过程和处理步骤:
1. 发现错误与中断管道
- 当 DataNode 2 发生故障(如宕机、网络不通、磁盘写满等),DataNode 1 会发现无法将数据发给 DataNode 2,或者客户端迟迟收不到来自 DataNode 2 的 ACK 确认包。
- 此时,当前的写入管道会被立即关闭。
- 客户端(DFSClient)将已经发送但未收到确认的数据包(Packets)重新放回数据队列的顶部,以确保数据不会丢失。
2. 剔除故障节点并更新标记
- 客户端会与 NameNode 进行通信,报告该节点故障。
- NameNode 会为当前正在写入的 Block 分配一个新的世代戳(Generation Stamp)。
- 为什么要分配新的世代戳? 因为发生故障的 DataNode 2 上可能残留了一部分不完整的数据块。当 DataNode 2 恢复后,NameNode 发现它上面的数据块的世代戳比最新的旧,就会将其视为“损坏或过期的数据”并将其删除。
- 发生故障的 DataNode 2 被从管道中剔除。
3. 构建新管道并恢复写入
- 剩下的健康节点(即 DataNode 1 和 DataNode 3)会相互通信,比较该 Block 的数据长度,并将它们的数据截断到相同的安全长度(确保大家的数据一致)。
- 客户端使用这两个剩下的健康节点(DataNode 1 -> DataNode 3)建立一条新的管道。
- 客户端继续从中断的地方向新管道写入数据。
4. 客户端写入成功判定
- 在 HDFS 中,只要写入成功的副本数达到了最小副本数要求(由参数
dfs.namenode.replication.min控制,默认值通常是 1),客户端的此次写操作就会被认为是成功的。 - 因为 DataNode 1 已经成功,所以客户端会正常完成写操作,不会抛出最终的失败异常。
5. NameNode 的异步补充复制
- 当客户端写完文件并调用
close()关闭文件后,NameNode 会检查该文件的块状态。 - NameNode 发现该 Block 的实际副本数(比如只有 2 个)小于目标副本数(配置的 3 个),就会将其标记为“欠佳复制(Under-replicated)”。
- 随后,NameNode 会在后台异步调度,选择集群中另一个健康的 DataNode,将缺失的副本从 DataNode 1 或 DataNode 3 复制过去,直到副本数恢复到 3 个。
补充说明(高级配置):
在 Hadoop 2.x 及之后的版本中,HDFS 引入了一个策略参数:dfs.client.block.write.replace-datanode-on-failure.policy。
- 默认情况下,如果管道中途有节点失败,客户端会继续用剩下的节点写完。
- 但如果是特殊配置(如
ALWAYS),或者剩余节点数少于总数的一半,客户端可能会在恢复管道时尝试向 NameNode 申请一个新的 DataNode 加入管道,以保证在写入过程中就维持 3 个副本。不过这取决于集群的具体配置,大多数默认情况下是先用剩余节点写完,再由 NameNode 事后补齐。