HDFS NameNode 工作机制:元数据到底是怎么存的?启动时发生了什么?
NameNode 是 HDFS 的大脑,但它不存数据,只存”账本”——文件在哪个目录、权限是什么、分成了几个块、每个块在哪些 DataNode 上。
这个账本要是丢了,整个集群就废了。所以 NameNode 的元数据管理是 HDFS 最核心的机制之一。
元数据为什么既要存内存,又要存磁盘?
NameNode 的元数据需要满足两个矛盾的要求:
- 要快:每次文件操作都要查元数据,必须放在内存里
- 要稳:节点挂了内存数据就没了,必须持久化到磁盘
所以 NameNode 的元数据分三份:
| 存储位置 | 内容 | 作用 |
|---|---|---|
| 内存 | 完整的目录树、文件属性、块映射 | 快速响应客户端请求 |
| FsImage(磁盘) | 某一时刻的元数据快照 | 持久化全量备份 |
| Edits(磁盘) | 操作日志(增量) | 记录 FsImage 之后的所有变更 |
关键理解:内存元数据 = FsImage + Edits 里所有操作。
FsImage:全量快照,但”块在哪”不存
FsImage 是元数据的全量快照文件,存的是某个时刻的文件系统状态。
但注意:FsImage 不存”每个块在哪个 DataNode 上”。 块的位置信息由 DataNode 启动时通过块报告上报给 NameNode,维护在内存中,不持久化到 FsImage。
怎么查看 FsImage 内容?
FsImage 是二进制文件,用 hdfs oiv 转成可读格式:
1 | 语法:hdfs oiv -p <格式> -i <FsImage文件> -o <输出文件> |
转换后能看到目录结构、文件属性、块 ID 和大小,但看不到块在哪个节点。
1 | <inode path="/testHdfs" type="DIRECTORY"> |
Edits:操作日志,实时追加
Edits 记录每一次元数据变更操作——创建文件、删除目录、修改权限、设置副本数。
写入流程:
- 客户端发起元数据修改操作
- NameNode 先把操作追加到 Edits 文件(磁盘)
- 再更新内存中的元数据
- 返回客户端成功
为什么先写 Edits 再更新内存? 如果先更新内存再写 Edits,写磁盘失败时内存已改但磁盘没记,数据不一致。先写磁盘保证了操作被持久化,即使后续内存更新失败也能恢复。
怎么查看 Edits 内容?
1 | 语法:hdfs oev -p <格式> -i <Edits文件> -o <输出文件> |
能看到每次操作的类型、路径、时间戳等。
1 | <RECORD> |
Edits 会无限增长,需要定期合并
Edits 不断追加,如果永远不合并,NameNode 启动时就要 replay 几个月甚至几年的操作日志,启动时间会变得不可接受。
解决方案:Secondary NameNode 定期合并 FsImage 和 Edits,生成新的 FsImage,然后 Edits 重置为空。
1 | FsImage(旧快照)+ Edits(增量操作)→ 合并 → 新 FsImage(最新状态) |
Secondary NameNode 的合并流程
Secondary NameNode 不是 NameNode 的备份,是”合并助手”。
sequenceDiagram
participant NN[NameNode]
participant 2NN[Secondary NameNode]
NN->>2NN: 触发 Checkpoint(定时或阈值)
2NN->>NN: 请求停止写入旧 Edits,创建临时 Edits(edits.new)
NN->>2NN: 发送当前 FsImage 和 Edits
2NN->>2NN: 加载 FsImage 到内存,replay Edits 生成新 FsImage(fsimage.ckpt)
2NN->>NN: 发送新 FsImage 和合并后的 Edits
NN->>NN: 替换旧 FsImage,将 edits.new 重命名为 Edits
Note over NN,2NN: 合并后,Edits 重置为空,FsImage 为最新状态
触发条件(满足其一就触发):
| 参数 | 默认值 | 说明 |
|---|---|---|
dfs.namenode.checkpoint.period |
3600秒 | 每 1 小时触发一次 |
dfs.namenode.checkpoint.txns |
1000000 | Edits 操作数达到 100 万次 |
实际运维中的注意点:
- 如果 Secondary NameNode 挂了,Edits 会一直增长,NameNode 重启时会非常慢
- 可以手动触发合并:
hdfs dfsadmin -rollEdits
NameNode 启动流程:从磁盘恢复到对外服务
启动时 NameNode 做四件事:
- 加载 FsImage:从磁盘读到内存,建立基础元数据
- Replay Edits:把 Edits 里的操作按顺序执行一遍,更新内存元数据到最新
- 生成新的 FsImage 和空的 Edits(Checkpoint)
- 进入安全模式:等待 DataNode 上报块信息,校验副本数
安全模式期间:
- 只读,不接受写请求
- NameNode 等待 DataNode 的块报告,检查每个块的副本数是否达标
- 达标比例超过 99.9% 且稳定一段时间后,自动退出安全模式
如果集群启动后一直卡在安全模式,通常是 DataNode 没起来或者副本数不足。
元数据高可用:多目录存储
FsImage 和 Edits 如果只存一份,磁盘坏了就全完了。
配置多个存储目录(hdfs-site.xml):
1 | <property> |
NameNode 会把元数据同时写入两个目录,一个坏了还有另一个。
生产环境建议: 至少配两个目录,最好一个在本地磁盘,一个在 NFS 或远程存储。
几个常见问题
1. Edits 过大导致启动慢怎么办?
检查 Secondary NameNode 是否正常工作。如果正常但 Edits 还是很大,说明合并间隔太长,可以调小 dfs.namenode.checkpoint.period。
如果 2NN 挂了,先恢复 2NN,或者手动 hdfs dfsadmin -rollEdits 强制合并。
2. FsImage 损坏了怎么办?
用 Secondary NameNode 上的 fsimage.ckpt(合并后的临时文件)替换损坏的 FsImage。如果 2NN 也没有,只能从 Edits 重新生成(如果有备份的话)。
3. NameNode 内存怎么设?
元数据全部在内存里。每个文件/目录大约占 150 字节,估算总文件数 × 150B,再留 2-3 倍余量。
比如 1 亿个文件 → 约 15GB,建议给 32GB-48GB 内存。
4. 元数据目录满了怎么办?
dfs.namenode.name.dir 所在的磁盘要监控剩余空间。FsImage 和 Edits 写不进去,NameNode 会直接挂掉。