HDFS 架构深入
概述
HDFS(Hadoop Distributed File System)是 Hadoop 的分布式文件系统,设计目标是"在廉价硬件上存储超大规模数据,并容忍节点故障"。理解 HDFS 的关键是三个问题:元数据与数据如何分离存储、数据如何保证不丢、系统如何容错。本文从角色分工出发,深入读写流程,再到 HA 高可用与联邦扩展。
一、核心角色与架构
1.1 三种角色的职责
┌────────────────────────┐
│ Client(客户端) │
└───────────┬────────────┘
│ 元数据请求 数据读写
▼ │
┌────────────────────────┐ │
│ NameNode(元数据) │ │
│ 目录树 / 文件块映射 │ │
└────────────────────────┘ │
│ ▼
┌───────────────────────────────────────────┐
│ DataNode 1 / DataNode 2 / ... / DataNode N │
│ 每个文件块存储多副本 │
└───────────────────────────────────────────┘| 角色 | 职责 | 状态 |
|---|---|---|
| NameNode | 管理元数据:目录结构、文件与数据块(Block)的映射、权限 | 无状态的数据不直接存文件 |
| DataNode | 存储实际数据块,定期向 NameNode 汇报块信息,响应读写请求 | 有状态 |
| SecondaryNameNode | 辅助合并 NameNode 的编辑日志(Edits)与镜像(FsImage) | 非热备 |
1.2 核心设计思想
| 设计 | 说明 |
|---|---|
| 元数据与数据分离 | NameNode 只管"地图",DataNode 管"货物",互不干扰 |
| 数据块 Block | 文件被切分为 128MB 的块,块是存储与复制的基本单位 |
| 多副本冗余 | 默认 3 副本,容忍节点故障 |
| 移动计算 | 数据在哪,计算就去哪,减少网络传输 |
二、NameNode:集群的大脑
2.1 元数据的存储结构
NameNode 的元数据保存在内存,同时持久化到磁盘两个文件:
| 文件 | 内容 | 特点 |
|---|---|---|
| FsImage | 元数据快照(目录树、块映射、属性) | 定期生成 |
| Edits Log | 操作日志(创建、删除、重命名等) | 实时追加,不断增长 |
NameNode 启动流程:
加载 FsImage → 回放 Edits Log → 重建完整元数据 → 进入安全模式等待 DataNode 汇报2.2 安全模式(SafeMode)
NameNode 启动后进入安全模式:
- 只读状态,禁止写操作
- 等待 DataNode 上报块信息,统计副本数满足的块比例
- 达到阈值(默认 0.999)自动退出安全模式
2.3 NameNode 的可用性问题
NameNode 是单点,一旦宕机整个集群不可用——这是 HDFS 设计上最大的软肋,解决方案就是后面要讲的 HA。
三、DataNode:数据的仓库
3.1 块存储与汇报
- 每个块默认 3 副本,副本放置策略:同机架两个副本 + 不同机架一个副本(默认策略)
- DataNode 每 3 秒发送心跳,每 10 个心跳汇报一次块报告(BlockReport)
- 10 分钟未收到心跳 → NameNode 判定节点死亡,触发副本复制补齐
3.2 块放置策略(机架感知)
机架 A 机架 B
┌─────────────────┐ ┌─────────────────┐
│ DataNode-1 副本1│ │ DataNode-4 副本3│
│ DataNode-2 副本2│ └─────────────────┘
└─────────────────┘
策略:副本1 本地节点,副本2 同机架另一节点,副本3 异机架节点
目的:容忍机架级故障,同时兼顾读写性能四、数据读写流程
4.1 读流程
1. Client 调用 DistributedFileSystem.open(path)
2. RPC 请求 NameNode:获取文件块及副本位置列表
3. 按"就近优先"原则选择 DataNode(本地 > 同机架 > 异机架)
4. 逐个读取块数据流(DFSInputStream),跨块时切换 DataNode
5. 读完所有块,验证校验和,拼装输出读的关键是就近读取——选择距离最近的副本,减少网络开销。
4.2 写流程
1. Client 调用 create(path),RPC 请求 NameNode 创建文件
2. NameNode 返回可写的 DataNode 列表(按副本放置策略)
3. 建立数据管线(Pipeline):Client → DN1 → DN2 → DN3
4. 数据按 64KB 包(Packet)沿管线流水式传输
每个节点收到后落盘并转发给下游,最后逐级 ACK
5. 全部块写完,Client 通知 NameNode 提交文件
6. 块以"最小副本数"为准视为写入完成(默认 1,可配置)写入管线示意:
Client ──Packet──▶ DN1 ──Packet──▶ DN2 ──Packet──▶ DN3
▲ │ │ │
└────ACK 逐级返回────┴────ACK───────┴────ACK──────────┘4.3 写入失败的容错
写入中途某个 DataNode 故障时:
- 数据管线中断,已确认的块保持
- 剩余节点重建管线继续写
- NameNode 检测到副本数不足后,异步复制补齐
五、副本策略与均衡
5.1 副本相关命令
bash
# 手动设置副本数(修改后 NameNode 异步调整)
hdfs dfs -setrep -w 3 /data/file
# 查看块与副本状态
hdfs fsck /data/file -files -blocks -locations
# 均衡器(重新平衡各节点数据分布)
hdfs balancer -threshold 105.2 数据不均衡的成因
- 节点扩容/缩容
- 大量新文件集中在部分节点
- 删除文件导致块分布不均
hdfs balancer 在后台搬迁块,让各节点使用率趋于一致。
六、HA 高可用
6.1 为什么需要 HA
NameNode 单点故障会让整个集群不可用,且重启需要回放大量 Edits,恢复时间长。HA 让两个 NameNode 一主一备,故障自动切换。
6.2 HA 架构组件
| 组件 | 作用 |
|---|---|
| Active NameNode | 提供服务,处理所有请求 |
| Standby NameNode | 热备,持续同步元数据,随时接管 |
| JournalNode | 共享编辑日志存储(Quorum,至少 3 个) |
| ZooKeeper 集群 | 协调故障自动切换(ZKFC) |
| Fencing 机制 | 防止脑裂(旧 Active 未真正死亡) |
ZooKeeper(ZKFC 协调)
▲
┌──────────┴──────────┐
▼ ▼
Active NameNode Standby NameNode
│ 双写 Edits │ 读取
└─────► JournalNode ◄──┘
(QJM 共享日志)6.3 工作流程
正常:Active 处理请求,写 Edits 到 JournalNode
Standby 实时从 JournalNode 读 Edits,保持元数据同步
Standby 同时接收 DataNode 块汇报,随时可接管
故障:ZKFC 检测到 Active 失联 → 触发 Fencing(隔离旧节点)
→ Standby 提升为 Active → 开始服务6.4 配置要点
xml
<!-- core-site.xml -->
<property>
<name>fs.defaultFS</name>
<value>hdfs://mycluster</value>
</property>
<!-- hdfs-site.xml -->
<property>
<name>dfs.nameservices</name>
<value>mycluster</value>
</property>
<property>
<name>dfs.ha.namenodes.mycluster</name>
<value>nn1,nn2</value>
</property>
<property>
<name>dfs.namenode.rpc-address.mycluster.nn1</name>
<value>node1:8020</value>
</property>
<property>
<name>dfs.namenode.rpc-address.mycluster.nn2</name>
<value>node2:8020</value>
</property>
<property>
<name>dfs.namenode.shared.edits.dir</name>
<value>qjournal://node1:8485;node2:8485;node3:8485/mycluster</value>
</property>七、联邦架构(Federation)
当 NameNode 元数据成为瓶颈时(内存上限、单点吞吐),联邦把命名空间横向拆分:
Namespace 1(/user)→ NameNode 1
Namespace 2(/data)→ NameNode 2
Namespace 3(/app) → NameNode 3
共享 DataNode 池| 方案 | 解决的问题 | 局限 |
|---|---|---|
| 垂直扩容 | 单 NN 内存不足 | 硬件有上限 |
| 联邦 | 命名空间拆分、NN 各自独立 | 目录归属需规划,跨命名空间操作受限 |
联邦与 HA 可叠加:每个命名空间都可以配一对主备 NameNode。
八、常见问题排查
| 现象 | 原因 | 处理 |
|---|---|---|
| 长时间处于安全模式 | 块副本比例未达标或块损坏 | hdfs dfsadmin -safemode leave 前先检查块健康 |
| 写入报空间不足 | 节点磁盘满 | 清理数据或扩容节点 |
| 读取慢 | 副本分布不均 / 网络带宽瓶颈 | 运行 balancer、检查机架感知配置 |
| NameNode 宕机恢复慢 | Edits 过大 | 配置 SNN/备份合并周期,HA 场景由 Standby 接管 |
| 数据节点下线后副本不足 | 复制带宽受限 | 调整 dfs.namenode.replication.max-streams |
九、小结
| 维度 | 要点 |
|---|---|
| 架构 | 元数据与数据分离,NameNode + DataNode + 客户端 |
| 存储 | 128MB 块 + 3 副本 + 机架感知放置 |
| 写入 | 数据管线流水式传输 + 逐级 ACK |
| 读取 | 就近原则选择副本 |
| 可靠性 | 心跳/块报告 + 副本补齐 + 校验和 |
| 可用性 | HA(QJM + ZKFC)解决单点,联邦解决扩展 |
参考链接: