1 HDFS 的数据流
1.1 HDFS 写数据流程
在 HDFS 中,文件写入并不是像单机文件系统那样“一次落盘”,而是一个涉及 客户端 → NameNode → 多个 DataNode 的协作过程。HDFS 会把大文件切分成若干 Block(默认 128MB),并在多个 DataNode 之间分布存储,每个 Block 会保留 多副本(默认 3 个)来保证可靠性。
1.1.1 剖析文件写入
写入的完整步骤:
- 客户端请求
- 客户端向 NameNode 发送写请求。
- NameNode 检查是否有写权限、文件是否已存在。
- NameNode 分配存储节点
- 返回一组 DataNode 列表,用来存放文件的 Block 副本。
- 客户端开始写入
- 客户端将文件切分成若干 Block。
- 第一个 Block 写入第一个 DataNode。
- 流水线复制
- 第一个 DataNode 会把数据转发给第二个,第二个再转发给第三个。
- 直到所有副本写入完成。
- 确认 (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 的模式。但与写入不同的是,读操作更强调 副本选择策略 和 并行化读取。
读取流程分解:
- 客户端发起请求
- 客户端向 NameNode 请求某个文件。
- NameNode 检查元数据,返回该文件各个 Block 的位置(哪些 DataNode 上存有副本)。
- 选择最近的副本
- 客户端根据机架感知(Rack Awareness)优先选择“最近”的 DataNode:
- 本地节点 > 同机架节点 > 跨机架节点。
- 客户端根据机架感知(Rack Awareness)优先选择“最近”的 DataNode:
- 分块并行读取
- 客户端为文件的每个 Block 建立读取线程,可以并行地从不同 DataNode 拉取数据。
- Block 在客户端本地拼接成完整文件。
- 数据校验
- 每个 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 拉取 FsImage 和 Edits,并进行合并。
- 将合并后的新 FsImage 回传给 NN,帮助其减轻重启时的压力。
- 它并不是 NN 的热备机,而是一个“日志打包器”。
ASCII 图:
┌───────────────┐
│ NameNode │
│ FsImage + Edits │
└───────┬───────┘
│ 定期合并
▼
┌───────────────┐
│Secondary NN │
│ 生成新 FsImage │
└───────────────┘
2.2 FsImage 和 Edits 解析
- FsImage:文件系统元数据的快照(比如“目录树”状态)。
- Edits:操作日志(新增文件、删除文件、修改权限等)。
启动时流程:
- 加载 FsImage。
- 顺序回放 Edits。
- 得到最新的命名空间状态。
表格对比:
文件 | 内容 | 存储位置 |
---|---|---|
FsImage | 全量元数据快照 | NN 磁盘 |
Edits | 增量操作日志 | NN 磁盘 |
CheckPoint | FsImage + Edits 合并结果 | 2NN 磁盘 |
2.3 CheckPoint 时间设置
如果 Edits 太大,NN 重启时回放会很慢。为此,2NN 会定期执行 CheckPoint。
- 触发条件
- 时间间隔:默认 1 小时。
- Edits 大小:达到阈值时触发。
- 过程
- 从 NN 拉取 FsImage 和 Edits。
- 在本地合并,生成新的 FsImage。
- 将新 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 的流程:
- 在新节点上安装 Hadoop,配置好
core-site.xml
和hdfs-site.xml
。 - 启动 DataNode 进程。
- NameNode 接收其心跳后,将其纳入集群。
- 使用
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) |
---|---|---|
部署 | 自建集群,需运维 | 云服务,免运维 |
一致性 | 强一致性 | 最终一致性 |
成本 | 硬件投入+运维成本 | 按需付费 |
最佳场景 | 大数据计算、批处理 | 非结构化数据归档/分发 |
💡 面试思考
- 为什么 HDFS 适合大文件,不适合小文件?
👉 因为 NameNode 内存中保存元数据,大量小文件会拖垮 NN。 - 写流程中如果第二个 DataNode 挂了,怎么办?
👉 NameNode 会重新选择一个新的 DN 来补齐副本。 - HA 架构中 Standby NN 如何保持元数据一致?
👉 依赖 JournalNode + Zookeeper 同步日志,保证切换时一致性。
🏁 小结
HDFS 已经诞生十余年,但它的设计思想依旧经典:
- 简单(副本容错)
- 高效(流水线复制 + 并行读写)
- 实用(机架感知 + SafeMode)
理解 HDFS,不仅是掌握 Hadoop 的第一步,也是理解分布式存储系统的基石。
Comments