1 HDFS 的数据流

1.1 HDFS 写数据流程

在 HDFS 中,文件写入并不是像单机文件系统那样“一次落盘”,而是一个涉及 客户端 → NameNode → 多个 DataNode 的协作过程。HDFS 会把大文件切分成若干 Block(默认 128MB),并在多个 DataNode 之间分布存储,每个 Block 会保留 多副本(默认 3 个)来保证可靠性。

1.1.1 剖析文件写入

写入的完整步骤:

  1. 客户端请求
    • 客户端向 NameNode 发送写请求。
    • NameNode 检查是否有写权限、文件是否已存在。
  2. NameNode 分配存储节点
    • 返回一组 DataNode 列表,用来存放文件的 Block 副本。
  3. 客户端开始写入
    • 客户端将文件切分成若干 Block。
    • 第一个 Block 写入第一个 DataNode。
  4. 流水线复制
    • 第一个 DataNode 会把数据转发给第二个,第二个再转发给第三个。
    • 直到所有副本写入完成。
  5. 确认 (ACK)
    • 最后一个 DataNode 写入成功后,逐级返回 ACK,直到客户端收到确认。

ASCII 图:

Client
│ 写请求

NameNode ── 分配 ──▶ [DN1, DN2, DN3]


Block 流水线: Client → DN1 → DN2 → DN3


1.1.2 网络拓扑 + 节点距离计算

为了避免数据都存放在同一机架上,HDFS 引入了 机架感知(Rack Awareness) 策略。

副本放置策略:

  • 第一个副本:尽可能放在客户端本地节点(如果客户端不在集群内,则随机选择)。
  • 第二个副本:放在不同机架的一个节点,保证容灾能力。
  • 第三个副本:放在和第二个副本同机架但不同节点,提升读取效率。

表格总结:

副本编号放置位置目的
1客户端本地节点快速写入
2另一机架容灾冗余
3同机架不同节点性能与安全兼顾

1.1.3 机架感知(副本存储节点选择)

ASCII 图示:

机架1: [DN1]──[DN2]
机架2: [DN3]──[DN4]
机架3: [DN5]──[DN6]

写入文件:

Block A: DN1 (本地) + DN3 (跨机架) + DN4 (同机架)

这样一来,即使机架 1 整体宕机,数据仍能在机架 2 找到副本。


💡 面试补充

  • 问题:HDFS 为什么不适合小文件?
    • 因为每个文件都需要元数据(由 NameNode 管理),大量小文件会导致内存消耗过大。
  • 问题:为什么副本不是平均分布?
    • 本地优先、跨机架保障、再平衡性能,这是权衡后的最佳方案。

1.2 HDFS 读数据流程

与写入类似,HDFS 的读取同样遵循 客户端 → NameNode → DataNode 的模式。但与写入不同的是,读操作更强调 副本选择策略并行化读取

读取流程分解:

  1. 客户端发起请求
    • 客户端向 NameNode 请求某个文件。
    • NameNode 检查元数据,返回该文件各个 Block 的位置(哪些 DataNode 上存有副本)。
  2. 选择最近的副本
    • 客户端根据机架感知(Rack Awareness)优先选择“最近”的 DataNode:
      • 本地节点 > 同机架节点 > 跨机架节点。
  3. 分块并行读取
    • 客户端为文件的每个 Block 建立读取线程,可以并行地从不同 DataNode 拉取数据。
    • Block 在客户端本地拼接成完整文件。
  4. 数据校验
    • 每个 Block 自带 Checksum
    • 客户端校验数据完整性,如果发现副本损坏,会向 NameNode 请求另一个副本。

ASCII 图示:

Client

│ 读请求

NameNode ── 返回块位置 ──▶ [DN1, DN3, DN4]

└── 客户端选择最近的 DataNode


DN3 ── 传输数据 ──▶ Client

多个 Block 并行拉取:

Block1 → DN1 → Client
Block2 → DN3 → Client
Block3 → DN4 → Client


表格:写与读的差异对比

操作客户端角色NameNode 角色DataNode 角色
写入发送写请求,分块写入分配 DataNode,记录元数据接收 Block,流水线复制
读取发送读请求,选择副本返回块位置提供 Block,校验数据

💡 面试补充

  • 为什么客户端要优先选择最近的副本?
    👉 减少网络传输,提高读性能。
  • 如果某个副本损坏怎么办?
    👉 客户端会自动尝试下一个副本,同时 NameNode 标记该副本无效,并触发 副本修复
  • 为什么 HDFS 读性能高?
    👉 主要得益于 并行读 + 副本选择优化

2 NameNode 和 SecondaryNameNode

在 HDFS 中,NameNode(NN) 是整个系统的“大脑”,负责管理文件系统的元数据;而 SecondaryNameNode(2NN) 是它的“秘书”,主要负责合并日志,减轻 NN 的压力。二者的关系常被误解,下面逐一解析。


2.1 NN 和 2NN 工作机制

  • NameNode (NN)
    • 保存文件系统命名空间(目录结构、文件名、权限)。
    • 维护 元数据(文件 → Block → DataNode 的映射)。
    • 响应客户端请求(读、写、删除)。
  • Secondary NameNode (2NN)
    • 定期从 NN 拉取 FsImageEdits,并进行合并。
    • 将合并后的新 FsImage 回传给 NN,帮助其减轻重启时的压力。
    • 它并不是 NN 的热备机,而是一个“日志打包器”。

ASCII 图:

┌───────────────┐
│ NameNode │
│ FsImage + Edits │
└───────┬───────┘
│ 定期合并

┌───────────────┐
│Secondary NN │
│ 生成新 FsImage │
└───────────────┘


2.2 FsImage 和 Edits 解析

  • FsImage:文件系统元数据的快照(比如“目录树”状态)。
  • Edits:操作日志(新增文件、删除文件、修改权限等)。

启动时流程:

  1. 加载 FsImage。
  2. 顺序回放 Edits。
  3. 得到最新的命名空间状态。

表格对比:

文件内容存储位置
FsImage全量元数据快照NN 磁盘
Edits增量操作日志NN 磁盘
CheckPointFsImage + Edits 合并结果2NN 磁盘

2.3 CheckPoint 时间设置

如果 Edits 太大,NN 重启时回放会很慢。为此,2NN 会定期执行 CheckPoint。

  • 触发条件
    • 时间间隔:默认 1 小时。
    • Edits 大小:达到阈值时触发。
  • 过程
    1. 从 NN 拉取 FsImage 和 Edits。
    2. 在本地合并,生成新的 FsImage。
    3. 将新 FsImage 推回 NN。

2.4 NameNode 故障处理(扩展点)

  • 单点故障风险:NN 挂掉 → 集群无法访问。
  • 解决方案
    • 冷备份(人工恢复)。
    • HA 架构:Active NN + Standby NN + JournalNode + Zookeeper。

ASCII 图:HA 架构简化

Client

┌───▼───────────┐
│ Zookeeper │ ← 监控 NN 状态
└───▲───────────┘

┌───┴───────┐ ┌────────┐
│ Active NN │←→│ Standby │
└───────────┘ └────────┘


JournalNode 集群


2.5 集群安全模式

  • NN 启动时进入 SafeMode(安全模式)。
  • 特点:只读,客户端无法写入。
  • 目的:等待 DataNode 上报 Block 信息,确保数据完整性。

常用命令:

# 手动退出安全模式
hdfs dfsadmin -safemode leave


2.6 NameNode 多目录配置(了解)

为防止磁盘损坏导致 NN 数据丢失,可以配置多个目录。

<property>
<name>dfs.namenode.name.dir</name>
<value>/data/nn1,/data/nn2</value>
</property>

这样,FsImage 和 Edits 会写入多个目录,提升可靠性。


💡 面试补充

  • 为什么需要 Secondary NameNode?
    👉 它是为了解决 NN 的 EditLog 文件无限膨胀问题,而不是备份 NN。
  • NN 的内存压力在哪?
    👉 大量小文件会导致 NN 的元数据过大(每个文件几百字节元数据,百万小文件就能撑爆内存)。
  • HA 架构和 2NN 有关系吗?
    👉 有了 HA,2NN 角色逐渐被替代,Checkpoint 由 Standby NN 完成。

3 DataNode(面试开发重点)

在 HDFS 中,DataNode(DN) 是存储层的“砖瓦工”,负责实际数据块(Block)的存储与读写。与 NameNode 相比,它更像是一群执行者,靠 心跳(Heartbeat)块报告(Block Report) 与 NN 保持联系。


3.1 DataNode 工作机制

  • 数据存储:接收客户端或其他 DataNode 发来的 Block,存放在本地磁盘。
  • 心跳:每隔 3 秒(默认)向 NN 发送一次心跳,报告自己存活。
  • 块汇报:周期性(默认 1 小时)上报本机所有数据块列表。
  • 读写请求:当客户端请求数据时,直接从 DN 把 Block 流式传回。

ASCII 图:

Client → NameNode → (返回DN位置信息) → DataNode
↑ │
└────────────── 数据读取 ────────────────┘


3.2 数据完整性

HDFS 采用 校验和(Checksum) 确保数据正确性:

  • 每个 Block 被拆分成若干个小块(chunk),每个小块计算校验值。
  • 当客户端读取时,会比对校验和,发现错误则自动从其他副本读取。
  • 坏块会被 NameNode 标记,并触发 副本修复

表格:常见完整性保障机制

机制说明触发时机
Checksum数据读写时校验每次读写
Block Report汇报 DN 上的块状态默认 1 小时
副本修复NameNode 触发复制坏块到新 DN异常检测后

3.3 掉线时限参数设置

如果 DN 在一段时间内不再发送心跳,NN 会认为它掉线。

  • dfs.heartbeat.interval:心跳间隔(默认 3 秒)。
  • dfs.namenode.heartbeat.recheck-interval:多久未收到心跳视为掉线(默认 10 分钟)。

掉线后的处理:

  • NN 将该 DN 标记为“死亡节点”。
  • 会在其他存活 DN 上复制丢失的 Block,保证副本数。

3.4 服役新数据节点

新增 DataNode 的流程:

  1. 在新节点上安装 Hadoop,配置好 core-site.xmlhdfs-site.xml
  2. 启动 DataNode 进程。
  3. NameNode 接收其心跳后,将其纳入集群。
  4. 使用 hdfs balancer 将数据均衡到新节点。

3.5 退役旧数据节点

如果一个 DN 硬件老化或需要下线,需要优雅退役。

3.5.1 添加白名单和黑名单

  • 白名单:允许加入集群的 DN 列表。
  • 黑名单:待退役的 DN。

通过编辑配置文件并刷新 NN 生效。

3.5.2 黑名单退役

  • 将节点加入黑名单后,NN 不再分配新任务给它。
  • NN 会把它存储的数据块复制到其他节点。
  • 数据安全复制完成后,节点可以安全下线。

3.6 DataNode 多目录配置

为了更好利用磁盘,可以配置 DN 使用多个目录:

<property>
<name>dfs.datanode.data.dir</name>
<value>/disk1/dfs,/disk2/dfs,/disk3/dfs</value>
</property>

好处:

  • 数据分散在不同磁盘,读写并行化。
  • 单块磁盘损坏不会导致 DN 整体不可用。

💡 面试补充

  • DN 为什么是无状态的?
    👉 它存储的数据块位置都由 NN 统一管理,DN 挂掉随时可替换。
  • 如果一个 DN 同时丢了多个 Block,会怎么样?
    👉 NN 会重新调度副本复制,保证每个 Block 的副本数。
  • 为什么需要 Balancer?
    👉 新增/退役节点后,数据分布不均,需要 hdfs balancer 平衡。

4 总结

通过本篇文章,我们从 数据流、NameNode 与 SecondaryNameNode、到 DataNode 机制,逐步剖析了 HDFS 的核心设计理念与实际运维要点。

核心要点回顾:

  • 数据流
    • 写入采用 流水线复制,副本分布遵循 机架感知策略,既保证性能又提升容灾能力。
    • 读取优先选择最近副本,并行下载多个 Block,借助 Checksum 保证数据完整性。
  • NameNode 与 SecondaryNameNode
    • NameNode 是 HDFS 的“大脑”,维护全局元数据。
    • SecondaryNameNode 的作用不是备份,而是负责定期 Checkpoint,帮助 NN 控制日志膨胀。
    • 安全模式(SafeMode)、FsImage/Edits 文件和 HA 架构是面试中的高频考点。
  • DataNode
    • 实际存储 Block,依赖心跳和块报告与 NN 通信。
    • 数据完整性通过 Checksum + 副本修复 来保障。
    • 新节点的服役、旧节点的退役,都要结合白名单/黑名单与 hdfs balancer 使用。

HDFS 的工程智慧

HDFS 的设计体现了“工程上的取舍”:

  • 放弃对小文件的高效支持,换取大文件顺序读写的极致性能。
  • 采用副本冗余(Replication)而不是复杂一致性协议,实现了“足够简单、足够可靠”的容错机制。
  • 引入机架感知,让数据分布兼顾 性能安全性

与现代存储的对比

  • 大数据计算平台(Hadoop、Spark、Flink)中,HDFS 依然是经典存储方案。
  • 云原生环境,对象存储(S3、OSS、COS)逐渐替代部分场景,但 HDFS 的思想(冗余、机架感知、流水线复制)仍值得借鉴。

表格:HDFS vs 对象存储

特性HDFS对象存储 (S3/OSS)
部署自建集群,需运维云服务,免运维
一致性强一致性最终一致性
成本硬件投入+运维成本按需付费
最佳场景大数据计算、批处理非结构化数据归档/分发

💡 面试思考

  1. 为什么 HDFS 适合大文件,不适合小文件?
    👉 因为 NameNode 内存中保存元数据,大量小文件会拖垮 NN。
  2. 写流程中如果第二个 DataNode 挂了,怎么办?
    👉 NameNode 会重新选择一个新的 DN 来补齐副本。
  3. HA 架构中 Standby NN 如何保持元数据一致?
    👉 依赖 JournalNode + Zookeeper 同步日志,保证切换时一致性。

🏁 小结

HDFS 已经诞生十余年,但它的设计思想依旧经典:

  • 简单(副本容错)
  • 高效(流水线复制 + 并行读写)
  • 实用(机架感知 + SafeMode)

理解 HDFS,不仅是掌握 Hadoop 的第一步,也是理解分布式存储系统的基石。