基于本文回答
0
评论

在HDFS中,追加(Append)数据的流程是怎样的?

知识点图片

在HDFS中,追加(Append)数据是指向一个已经存在且已关闭的文件末尾继续写入数据的过程。

HDFS最初的设计是“一次写入,多次读取”(WORM),不支持修改和追加。但在后来的版本中(Hadoop 2.x及以后),为了支持HBase的WAL(预写日志)和Flume等流式数据写入场景,HDFS引入并完善了Append功能。

HDFS追加数据的核心难点在于处理文件的最后一个数据块(Block)。追加数据的整体流程可以分为四个主要阶段:请求追加、建立/恢复流水线、数据传输、完成与关闭

以下是详细的流程步骤:

第一阶段:请求追加(NameNode交互)

  1. 客户端发起请求:客户端(Client)调用 FileSystem.append(Path f) 方法,向 NameNode 发起 Append 的 RPC 请求。
  2. NameNode 校验
    • NameNode 检查文件是否存在。如果文件不存在或是一个目录,则报错。
    • 检查客户端是否有对该文件的写权限。
    • 检查文件的租约(Lease)。HDFS保证同一时刻只有一个客户端能写入文件。如果文件当前被其他客户端打开(持有租约),则请求失败。NameNode 会将该文件的租约分配给当前请求追加的客户端。
  3. 处理最后一个 Block
    这是 Append 与普通 Write 最大的不同点。NameNode 检查文件的最后一个 Block 是否已满(通常是 128MB):
    • 如果最后一个 Block 已满:NameNode 会像普通的写操作一样,为该文件分配一个新的 Block,并返回给客户端。
    • 如果最后一个 Block 未满(处于 Partial 状态):NameNode 会将该 Block 的状态从 COMPLETE(完成)修改为 UNDER_CONSTRUCTION(构建中)。同时,NameNode 会为该 Block 生成一个新的版本号(Generation Stamp, GS),以防止旧的或损坏的副本干扰。
  4. 返回位置信息:NameNode 将最后一个 Block 的信息(包括所在的 DataNode 列表、新的 GS、当前的数据长度等)封装在 LocatedBlock 对象中,返回给客户端。

第二阶段:恢复与建立流水线(Pipeline Setup)

如果最后一个 Block 未满,客户端需要连接保存该 Block 副本的 DataNode,重新建立数据传输流水线。

  1. 客户端联系 Primary DataNode:客户端根据 NameNode 返回的 DataNode 列表,联系第一个 DataNode(Primary DataNode)。
  2. DataNode 状态同步与恢复
    • Primary DataNode 会与其他存放该副本的 DataNode 通信。
    • 它们会比对各自磁盘上该 Block 的实际长度,协商出一个一致的最小长度(Truncate 操作,丢弃可能存在的不一致的脏数据)。
    • 所有正常的 DataNode 会将该 Block 的版本号(GS)更新为 NameNode 分配的新版本号。
  3. 建立 Pipeline:DataNode 之间建立起数据传输的流水线(Client -> DN1 -> DN2 -> DN3)。如果有 DataNode 故障,客户端会向 NameNode 报告,并剔除故障节点,使用剩余的节点建立流水线。

第三阶段:数据传输(Data Streaming)

  1. 追加数据:客户端开始将要追加的数据切分成一个个 Packet(通常为 64KB)。
  2. 发送数据:Packet 通过构建好的 Pipeline 依次传输(Client 发给 DN1,DN1 发给 DN2,DN2 发给 DN3)。
  3. 接收确认(ACK):DataNode 将数据写入本地磁盘后,会按相反的顺序沿 Pipeline 返回 ACK 确认包给客户端。
  4. 跨 Block 写入
    • 当追加的数据填满了当前的最后一个 Block(达到 128MB)后,当前的 Pipeline 会关闭。
    • 客户端会再次向 NameNode 申请一个新的 Block。
    • NameNode 分配新 Block 并返回新的 DataNode 列表,客户端建立新的 Pipeline 继续写入剩余数据(这部分流程与普通的文件创建写入完全一致)。

第四阶段:完成与关闭(Close)

  1. 数据刷盘:客户端发送完所有数据,并收到最后一个 Packet 的 ACK 确认后,通知 DataNode 完成当前 Block 的写入(Finalize Block)。
  2. 通知 NameNode:客户端调用 close() 方法,向 NameNode 发送 complete RPC 请求。
  3. NameNode 更新元数据
    • NameNode 确认该文件的所有 Block(除了可能正在写入的最后一个新 Block)的副本数达到最小要求。
    • 将 Block 的状态从 UNDER_CONSTRUCTION 改回 COMPLETE
    • 更新文件的最终长度、修改时间等元数据信息。
    • 释放该文件的写租约(Lease),允许其他客户端进行操作。

总结:Append 与普通 Write 的核心区别

特性 普通写入 (Create) 追加写入 (Append)
文件状态 创建新文件或覆盖旧文件。 必须是已存在的、当前处于关闭状态的文件。
NameNode操作 分配第一个全新的 Block。 查找最后一个 Block,将其状态改为 UNDER_CONSTRUCTION
DataNode操作 直接在新节点上创建新 Block。 找到原有的 DataNode,进行Block恢复和长度同步,更新Generation Stamp。
使用场景 批量导入静态数据。 HBase WAL、Flume日志收集等流式持续写入场景。
右滑查看面试常问