0%

DataNode工作机制

HDFS DataNode 工作机制:数据怎么存、状态怎么报、坏了怎么办

DataNode 是 HDFS 里真正存数据的节点。NameNode 管”账本”,DataNode 管”货”。

但它不只是”存”就完了——还要定期向 NameNode 报平安、汇报自己手里有哪些数据块、自己检查数据有没有坏。这套机制保证了 HDFS 在廉价硬件上仍然可靠。

DataNode 的三大职责

职责 具体工作
存数据 把 Block 以文件形式存在本地磁盘上
报状态 定期告诉 NameNode “我活着”、”我手里有这些块”
自检 检查自己存的数据有没有损坏,坏了就上报

启动阶段:注册 + 第一次汇报

sequenceDiagram  
    participant DN[DataNode]  
    participant NN[NameNode]  
    DN->>NN: 发送注册请求(包含节点 ID、存储目录等信息)  
    NN->>DN: 验证注册信息,返回注册成功响应  
    DN->>DN: 扫描本地存储目录,收集所有数据块信息(Block ID、大小、校验和)  
    DN->>NN: 发送完整块报告(Block Report),包含所有块的元数据  
    NN->>DN: 确认块报告,更新集群块映射信息  
    Note over DN,NN: 初始化完成,DataNode 进入常态运行

DataNode 启动时做三件事:

1. 注册

向 NameNode 发送注册请求,包含节点 ID 和存储目录信息。NameNode 验证通过后,DataNode 正式加入集群。

DataNode 的节点 ID 存在本地文件里(dfs.datanode.data.dir/current/VERSION),每次启动用同一个 ID,避免 NameNode 把它当成新节点。

2. 扫描本地磁盘

扫描 dfs.datanode.data.dir 配置的所有目录,收集自己手里有哪些 Block。

3. 发送完整块报告

把扫描到的所有 Block 信息一次性发给 NameNode。NameNode 收到后更新内存中的块映射。

第一次块报告的数据量很大(一个 DataNode 可能有几万甚至几十万个 Block),所以 DataNode 启动后需要一点时间才能完成初始化。

常态运行:心跳 + 增量报告

启动完成后,DataNode 进入日常运行状态,主要做两件事:

1. 心跳(Heartbeat)——“我还活着”

默认每 3 秒向 NameNode 发一次心跳。NameNode 通过心跳知道这个节点还活着,同时在心跳响应里附带指令——“把块 A 复制一份到节点 X”、”把块 B 删掉”。

心跳停了,NameNode 就知道这个节点可能挂了。

2. 增量块报告——“我手里的块有变化”

完整块报告只在启动时发一次,日常只发送有变化的块——新增的、删除的、修改的。默认每 6 小时发一次。

这两种报告的频率对比:

报告类型 频率 内容
心跳 3 秒 节点存活状态 + 接收指令
增量块报告 6 小时 自上次报告后有变化的块
完整块报告 仅启动时 所有块(一次性)

数据完整性保障:校验和机制

DataNode 存的数据可能会坏——磁盘坏道、网络传输错位、硬件老化。HDFS 靠校验和(CheckSum)来检测数据是否损坏。

原理很简单:

  • 写入数据时,每写一个 Block,同时计算一个校验和(默认 CRC32C 算法),存成一个 .meta 文件
  • 读取数据时,重新计算校验和,跟 .meta 文件里的对比
  • 对不上 → 数据坏了

验证时机:

  1. 读取时验证:客户端读数据时,DataNode 自动校验
  2. 定期扫描:DataNode 后台线程定期扫描所有 Block(默认每 3 周一次)

发现损坏了怎么办?

DataNode 把坏块标记为损坏,向 NameNode 报告。NameNode 从其他副本复制一份到这个节点,恢复副本数。

这个机制的意义: HDFS 用的是廉价硬盘,硬盘会坏。校验和机制让 DataNode 自己发现坏块并触发修复,不需要人工介入。

故障检测:NameNode 怎么知道 DataNode 死了?

DataNode 如果超过一定时间不发送心跳,NameNode 就把它标记为”死亡”。

超时计算公式:

1
超时时间 = 2 × recheck-interval + 10 × heartbeat-interval

默认值:

  • heartbeat.interval = 3 秒
  • recheck-interval = 5 分钟(300000 毫秒)

默认超时 = 2 × 300 秒 + 10 × 3 秒 = 630 秒(10.5 分钟)

也就是说,DataNode 超过 10.5 分钟没发心跳,NameNode 就判定它挂了。

为什么不设更短? 网络抖动可能导致节点”假死”,设太短会频繁触发副本复制,浪费资源。

故障恢复:节点挂了,数据怎么办?

NameNode 判定一个 DataNode 死亡后,自动启动恢复流程:

  1. 把这个节点上的所有 Block 标记为”副本不足”(Under-Replicated)
  2. 对每个副本不足的 Block,找其他有副本的节点复制一份
  3. 确保每个 Block 的副本数恢复到配置值(默认 3)

这个过程的代价不小——大量数据需要跨节点复制,网络带宽会吃紧。所以一个稳定运行的集群,DataNode 不要频繁上下线。

存储目录:数据怎么放在磁盘上

DataNode 的数据存储目录通过 dfs.datanode.data.dir 配置,支持多目录:

1
2
3
4
<property>
<name>dfs.datanode.data.dir</name>
<value>file:///hadoop/hdfs/data1,file:///hadoop/hdfs/data2</value><!-- 多磁盘目录 -->
</property>

多目录的好处:

  • 数据均匀分布到各磁盘,避免单盘过载
  • 一块磁盘坏了,只影响那上面的 Block,其他磁盘不受影响

目录结构:

1
2
3
4
5
6
7
8
/hadoop/hdfs/data/current/  
├── BP-123456789-192.168.1.1-1234567890123/ <!-- 块池目录(Block Pool) -->
│ ├── current/
│ │ ├── blk_123456789 <!-- 数据块文件(实际数据) -->
│ │ ├── blk_123456789_1234.meta <!-- 校验和文件 -->
│ │ └── subdir0/ <!-- 块文件子目录(避免单目录文件过多) -->
│ └── VERSION <!-- 块池元数据(如集群 ID、块池 ID) -->
└── VERSION <!-- DataNode 节点元数据(如节点 ID、集群 ID) -->

Block 文件分散在 subdir0subdir1… 子目录下,避免单个目录下文件数量过多导致性能下降。

几个实际运维中的注意点

1. 磁盘满了怎么办?

DataNode 磁盘写满,NameNode 不会自动处理,但 DataNode 会拒绝写入新块。监控磁盘使用率,超过 80% 就该考虑扩容或清理数据。

2. 块报告太大,NameNode 处理不过来?

如果集群规模很大(上千节点),完整块报告同时发送会压垮 NameNode。可以通过 dfs.blockreport.split.threshold 把块报告拆成小块分批发送。

3. DataNode 启动慢?

启动时要扫描所有磁盘,扫描几百万个 Block 可能需要十几分钟。正常现象,不要以为卡住了就重启。

4. 副本不足报警怎么处理?

hdfs fsck / 会显示有多少 Block 副本不足。如果持续存在,说明 DataNode 节点不够或者有节点故障了。先检查集群节点状态,再考虑增加节点或降低副本数。

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