0%

NameNode工作机制

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
2
# 语法:hdfs oiv -p <格式> -i <FsImage文件> -o <输出文件>  
hdfs oiv -p XML -i /hadoop/hdfs/name/current/fsimage_xxx -o ~/fsimage.xml

转换后能看到目录结构、文件属性、块 ID 和大小,但看不到块在哪个节点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<inode path="/testHdfs" type="DIRECTORY">  
<id>16385</id>
<permissions>zhanghe:supergroup:rwxr-xr-x</permissions>
<modificationTime>1623523456789</modificationTime>
</inode>
<inode path="/testHdfs/data.txt" type="FILE">
<id>16386</id>
<replication>3</replication>
<modificationTime>1623523467890</modificationTime>
<block>
<id>1073741825</id>
<genstamp>1001</genstamp>
<numBytes>134217728</numBytes> <!-- 128MB 块 -->
</block>
</inode>

Edits:操作日志,实时追加

Edits 记录每一次元数据变更操作——创建文件、删除目录、修改权限、设置副本数。

写入流程:

  1. 客户端发起元数据修改操作
  2. NameNode 先把操作追加到 Edits 文件(磁盘)
  3. 再更新内存中的元数据
  4. 返回客户端成功

为什么先写 Edits 再更新内存? 如果先更新内存再写 Edits,写磁盘失败时内存已改但磁盘没记,数据不一致。先写磁盘保证了操作被持久化,即使后续内存更新失败也能恢复。

怎么查看 Edits 内容?

1
2
# 语法:hdfs oev -p <格式> -i <Edits文件> -o <输出文件>  
hdfs oev -p XML -i /hadoop/hdfs/name/current/edits_xxx -o ~/edits.xml

能看到每次操作的类型、路径、时间戳等。

1
2
3
4
5
6
7
8
9
<RECORD>  
<OPCODE>OP_CREATE</OPCODE> <!-- 创建文件操作 -->
<DATA>
<PATH>/testHdfs/newfile.txt</PATH>
<TIMESTAMP>1623523500000</TIMESTAMP>
<PERMISSIONS>0644</PERMISSIONS>
<REPLICATION>3</REPLICATION>
</DATA>
</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 做四件事:

  1. 加载 FsImage:从磁盘读到内存,建立基础元数据
  2. Replay Edits:把 Edits 里的操作按顺序执行一遍,更新内存元数据到最新
  3. 生成新的 FsImage 和空的 Edits(Checkpoint)
  4. 进入安全模式:等待 DataNode 上报块信息,校验副本数

安全模式期间:

  • 只读,不接受写请求
  • NameNode 等待 DataNode 的块报告,检查每个块的副本数是否达标
  • 达标比例超过 99.9% 且稳定一段时间后,自动退出安全模式

如果集群启动后一直卡在安全模式,通常是 DataNode 没起来或者副本数不足。

元数据高可用:多目录存储

FsImage 和 Edits 如果只存一份,磁盘坏了就全完了。

配置多个存储目录(hdfs-site.xml):

1
2
3
4
5
6
7
<property>
<name>dfs.namenode.name.dir</name>
<value>
file:///hadoop/hdfs/name,
hdfs://namenode:8020/namenode/backup <!-- 远程备份目录 -->
</value>
</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 会直接挂掉。

欢迎关注我的其它发布渠道