3、HDFS数据块机制:默认块大小为何是128MB、块副本机制与机架感知、块存储与元数据管理

3.1 默认块大小为何是128MB?

很多刚接触HDFS的同学都会问:为什么块大小是128MB?不是64MB,也不是256MB?

我刚开始也觉得很奇怪。传统的文件系统,块大小通常是4KB。HDFS一下子跳到128MB,差了3万多倍。这背后其实有很深的考量。

先说结论:128MB是为了平衡寻址开销和传输效率

HDFS的设计目标,是处理超大文件。一个文件可能几百GB甚至TB级别。如果块太小,比如4KB,那一个1TB的文件就需要2.68亿个块。NameNode内存里要存2.68亿条元数据记录。我算过一笔账,每条元数据记录大约150字节,光元数据就要占掉40GB内存。这显然不现实。

反过来,块太大也不行。比如块大小设为1GB,那一个10MB的小文件,也要占1GB的存储空间。磁盘利用率会非常低。

核心公式:寻址时间 ≈ 10ms,传输速度 ≈ 100MB/s。要让寻址时间占比小于1%,块大小至少需要 10ms × 100MB/s = 1MB。实际上HDFS选择了128MB,留出了充足的余量。

我记得在2011年左右,Hadoop 2.0之前,默认块大小还是64MB。后来社区做了大量测试,发现128MB在大多数场景下表现更好。尤其是SSD普及之后,磁盘顺序读写速度大幅提升,128MB能更好地利用带宽。

我的建议:如果集群以机械硬盘为主,可以保持128MB不变。如果全闪存集群,可以尝试256MB。我曾在全闪集群上测试过,256MB块大小下,大文件读取性能提升了15%左右。

3.2 块副本机制与机架感知

HDFS的副本机制,说白了就是「鸡蛋不放在一个篮子里」。默认3副本,每个块在集群里有3份拷贝。

但问题来了:这3份拷贝怎么放?

随机放肯定不行。万一3个副本都在同一个机架上,那个机架断电了,数据就全丢了。所以HDFS引入了机架感知

机架感知的放置策略是这样的:

  • 第一个副本:放在客户端所在的节点(如果客户端不在集群内,则随机选一个节点)
  • 第二个副本:放在不同机架上的随机节点
  • 第三个副本:放在第二个副本所在机架上的另一个节点

为什么要这么设计?

你想想看,如果第二个副本和第三个副本放在不同机架,那写数据时就要跨两个机架传输。网络开销会翻倍。而放在同一个机架内,只需要一次跨机架传输,一次机架内传输。既保证了容错性,又控制了网络流量。

我曾经踩过的坑:有一次集群扩容,新加了20个节点。我忘了配置机架拓扑脚本,结果所有副本都挤在同一个机架上。一个月后那个机架交换机故障,导致大量数据不可用。从那以后,我每次扩容第一件事就是检查机架感知配置。

机架感知的配置其实很简单。在 core-site.xml 里设置:

<property>
  <name>net.topology.script.file.name</name>
  <value>/etc/hadoop/conf/topology.sh</value>
</property>

topology.sh脚本的作用,就是把IP地址映射成机架路径。比如:

#!/bin/bash
# 简单的机架映射脚本
case $1 in
  192.168.1.*) echo "/rack1" ;;
  192.168.2.*) echo "/rack2" ;;
  *) echo "/default-rack" ;;
esac

嗯,这里要注意:脚本必须可执行,而且返回格式必须是 /rack-name 这种形式。否则HDFS会报错。

3.3 块存储与元数据管理

块存储和元数据管理,是HDFS的两大核心。一个管数据,一个管索引。

块存储发生在DataNode上。每个块在磁盘上存成两个文件:

  • blk_1073741825:块数据文件
  • blk_1073741825_1001.meta:校验元数据文件

校验文件里存的是CRC32校验和。每次读取块时,DataNode会重新计算校验和,跟meta文件里的对比。不一致就说明数据损坏了。

我遇到过一种情况:磁盘静默错误导致数据损坏,但校验和没变。这是因为CRC32只能检测部分错误。所以生产环境建议开启端到端校验,客户端写入时计算一次校验和,读取时再验证一次。

元数据管理是NameNode的活。NameNode内存里维护着三张表:

元数据表 存储内容 示例
文件命名空间 目录树结构、文件名、权限 /user/hive/warehouse/table1
文件到块映射 每个文件包含哪些块 file1 → block1, block2, block3
块到DataNode映射 每个块存储在哪些节点上 block1 → dn1, dn2, dn3

这三张表都保存在内存里,同时会持久化到磁盘上的两个文件:

  • fsimage:元数据快照
  • edits:增量操作日志

NameNode启动时,先把fsimage加载到内存,然后回放edits里的操作。这个过程叫检查点

关键点:fsimage和edits必须放在不同的磁盘上。我见过有人把两个文件放同一块盘,结果磁盘坏了,元数据全丢。NameNode挂了可以重启,元数据丢了就真的完了。

块存储和元数据管理的关系,就像图书馆的书架和索引卡。书架(DataNode)只管放书,索引卡(NameNode)记录每本书的位置。读者(客户端)先查索引卡,再去书架上取书。

这种设计的好处是:索引卡很小,可以放在内存里,查询速度极快。坏处是:索引卡一旦丢失,整个图书馆就瘫痪了。所以生产环境一定要配置高可用NameNode,用ZooKeeper做选主,JournalNode同步edits日志。

我的经验:元数据管理最怕的是内存不足。一个块大约占用150字节内存。如果集群有1亿个块,NameNode就需要15GB内存。建议给NameNode分配的内存是预估值的2倍,留出余量应对突发写入。

最后说一句:块存储和元数据管理,是HDFS的基石。理解透了这两个机制,后面学HDFS的读写流程、容错机制、负载均衡,都会轻松很多。