跳转至

HDFS 架构原理

作者:JiangChong | 撰写时间:2026年08月

适用场景:零基础认识 HDFS;或需要理解 HDFS 底层机制——比如排查 Vertica COPY 从 HDFS 加载慢、评估 HDFS 作为数据湖存储底座、或搭建大数据平台时,先读本文建立原理框架,再按需进入系列中更细的文章。

阅读前先认识三个词

术语 一句话解释
块(Block) HDFS 存储文件的基本单位,默认 128MB。一个文件被切成若干块,分散存在不同机器上
副本(Replica) 同一个块的多份拷贝(默认 3 份),存放在不同机器上。任何一台机器挂了,别的副本顶上
元数据(Metadata) 「描述数据的数据」——文件名、目录结构、每个块存在哪台机器。由 NameNode 专门管理

这三个词贯穿全文。完整的术语表见文末附录 A,随时可回来查。

关联文章

本系列 01~09 共 9 篇(本文为第一篇),另有 2 篇既有 Vertica 关联文章:

理解全文脉络

本文按「为什么需要 → 静态架构 → 动态机制 → 设计权衡 → 对比与实操」组织:第 0 节回答「HDFS 解决什么问题」,第 1 节讲三个角色各管什么,第 2、3 节深入元数据如何持久化,第 4、5 节讲节点之间如何通信,第 6、7 节回答两个高频问题——块为什么是 128MB、HDFS 和 NAS 到底差在哪,第 8 节用总结表收束,第 9 节给实践清单。

新手建议从头顺序读——每一节都从通俗比喻讲起,再进入机制细节。赶时间的读者可以只读第 0、1、8 节和附录 A,10 分钟建立全景。


0. 为什么需要 HDFS:从一个装不下的文件说起

假设你手头有 100TB 的日志数据要做分析,摆在面前的是三座大山:

  • 装不下:单台服务器挂 10 块 4TB 硬盘,总共也只有 40TB,物理上就放不下;
  • 读不完:就算放得下,按单机 100MB/s 的顺序读速度,完整读一遍要 10 天以上;
  • 算不动:想让 100 台机器同时读、同时算,文件却只躺在一台机器上,怎么分给它们?

HDFS 就是为这类问题发明的。它的思路朴素得近乎粗暴:把大文件切成块,撒到成百上千台廉价服务器的硬盘上,让这些机器既能并行存储、又能并行读写。存储不再是一台机器的能力,而是整个集群的能力。

graph LR
    A["大文件<br/>100TB"] --> B["切成块<br/>每块 128MB"]
    B --> C["撒到集群<br/>每块存 3 份"]
    C --> D["并行读写<br/>吞吐 ≈ 机器数 × 单机速度"]

HDFS 全称 Hadoop Distributed File System,即「Hadoop 分布式文件系统」。「分布式」指的就是上面这张图:文件不在一台机器上,而是分散在多台机器上。它是 Hadoop 生态的存储底座——MapReduce、Spark、Flink 处理的数据,绝大多数都躺在 HDFS(或同类系统)里。

对 Vertica 用户的一个直接联想:Vertica 把一个表按 hash 分布到各节点的投影里,HDFS 把一个大文件切成块分布到各节点——都是「用集群的规模换单机扛不住的体量」这个思路。后面 Vertica Eon Communal Storage 运维实战 会看到两者如何组合使用。


1. 整体架构:一个「只记台账、不搬货」的管家

HDFS 采用 Master/Slave(主从)架构,角色只有三类:NameNode(名字节点,通常 1 个或主备 2 个)、DataNode(数据节点,成百上千)、Client(客户端)。

把 HDFS 想象成一座大型仓库:

  • NameNode 是只记台账的仓库管理员——他清楚每个文件被切成几块、每块存放在哪个货架,但自己从不搬货;
  • DataNode 是搬运工,货(数据块)真正存在他们各自的硬盘里;
  • Client 是取货人,先问管理员「货在哪」,然后直接找搬运工拿货,管理员全程不参与搬运。
graph LR
    C["Client<br/>取货人"]
    C -->|"① 问:块在哪?(元数据请求)"| NN["NameNode<br/>仓库管理员:只记台账,不搬货"]
    C -->|"② 直接取货 / 送货(数据传输)"| DN1["DataNode-1<br/>搬运工"]
    C -->|"② 直接取货 / 送货"| DN2["DataNode-2<br/>搬运工"]
    DN1 -.->|"心跳 + 块报告(每 3 秒 / 6 小时)"| NN
    DN2 -.->|"心跳 + 块报告"| NN

这张图就是全文的总纲:元数据走 NameNode,数据走 DataNode,两条路分开。下面的 1.1~1.3 逐个展开。

1.1 NameNode:只存元数据,不存数据

NameNode 管理两类信息,持久化方式截然不同:

信息 内容 持久化方式
命名空间(namespace) 目录树、文件名、权限、副本数、文件→块列表 落盘:FSImage + EditLog(第 2 节详述)
块→DataNode 映射 每个块(block)的每个副本实际在哪台机器 仅内存,靠块报告重建(第 4 节详述)

关键结论:块→DataNode 的映射只存在 NameNode 内存中,重启后完全依赖 DataNode 主动汇报来重建。 这是理解 NameNode 重启慢、安全模式、以及心跳/块报告机制的总钥匙。

新手常问:为什么「块在哪」这么重要的信息不落盘?

因为这份映射变化得太频繁了。 一个块被复制、被删除、被 balancer 搬去别的节点,映射都要跟着改。如果每次都写盘,NameNode 会陷在磁盘 IO 里出不来;而 DataNode 本来就知道自己硬盘上有什么块,干脆让它们在 NameNode 重启时主动汇报一遍,重建映射——「内存里放最常变的信息,启动时从源头重新收集」,这是典型的以简单换可靠的分布式设计。

NameNode 是单点(SPOF)——没有 HA 时它一挂,整个集群不可用,所以它是 HDFS 里被「供养」得最精细的组件:专用堆内存、可靠性高的磁盘、以及一整套高可用方案(QJM+ZKFC,见系列 HA 文章)。

NameNode 的内存大小直接决定集群能存多少文件与块:官方口径每个块约占用 150 字节堆内存,100 万块约 150MB——单看不多,但一个 1PB、128MB 块的集群就有约 800 万个块;小文件场景更是动辄上亿个元数据对象。按 150 字节算,1 亿块 ≈ 15GB 堆内存,这就是「NameNode 吃内存」说法的来源,也是 HDFS 小文件问题与治理 整篇的伏笔。

1.2 DataNode:真正的「硬盘」

DataNode 负责四类工作:

  • 按块存储数据(块分散在 dfs.datanode.data.dir 配置的多个目录,可跨盘条带)
  • 响应 Client 的读写请求(数据不经过 NameNode,Client 与 DataNode 直连)
  • 周期性向 NameNode 汇报(心跳 + 块报告,第 4 节)
  • 执行 NameNode 下发的指令:复制副本、删除块、定期块扫描校验

物理形态上,一个块对应两个文件:数据文件 blk_<id> 与校验文件 blk_<id>_<genstamp>.meta(默认 CRC32C 校验)。如果你登录一台 DataNode 到数据目录下翻一翻,看到的不是整齐的原始文件,而是大量这种 blk_ 开头的块文件——文件进 HDFS 之后,原始形态就「消失」了,对外呈现的目录树完全是 NameNode 元数据的功劳。

块是独立的复制与调度单元——同一个文件的 3 个副本存放在 3 台不同机器上,任何一台挂了,其余副本照常服务,这正是「副本」这一概念的落点。

1.3 Client:问路,然后自己走

Client(Java 的 DFSClient 是主流实现,另有 libhdfs、WebHDFS REST 等)只做两件事:

  1. 读写前先向 NameNode 询问元数据——文件被切成哪些块、每块在哪台 DataNode 上
  2. 拿到块位置后,直接与 DataNode 建连传输数据,NameNode 完全不参与数据路径

「控制平面(元数据)与数据平面(数据流)分离」是 HDFS 吞吐量的根基:NameNode 每次应答只处理几字节的元数据,海量数据在 DataNode 之间并行流动,两者互不挤占。这也是它与「所有 IO 都过文件服务器」的传统 NAS 的本质差异(第 7 节对比)。

本节要点:三类角色一句话——NameNode 管「货在哪」(元数据),DataNode 存「货」(数据块),Client 先问路再自己取货(数据不经过 NameNode)。这张图记住了,HDFS 的一半就懂了。


2. 元数据持久化:FSImage 快照 + EditLog 流水账

NameNode 的命名空间元数据必须落盘,否则一次宕机就丢失全部文件清单。它用两样东西配合完成:

  • FSImage:某一时刻命名空间的完整快照(目录树、文件、块列表、权限、副本数)。相当于给整个文件系统「拍一张照」。
  • EditLog:快照之后发生的所有操作日志(mkdir、put、delete…),每条带单调递增的事务 ID(txid),以段(segment)存储,段名携带起止 txid。相当于「拍照之后的流水账」。

用记账打比方:FSImage 是年末的总账本,EditLog 是每天的流水单。年底总账 + 之后每天的流水 = 当前账目。

2.1 为什么不用单一文件?——把「状态」与「变更」分开

如果每次操作都直接改写 FSImage 这个大文件,每次修改都要整文件写盘(数 GB),NameNode 立刻成为吞吐瓶颈。于是设计成:操作只追加写 EditLog(顺序小写,非常快),FSImage 只在 checkpoint 时重建一次。

这正是数据库 WAL(Write-Ahead Log,预写日志)的通用思路——把随机改大文件,变成顺序追加小日志。随机改写就像「在一本大厚书中间涂改一页」,每次都要翻书找页码;顺序追加则像「在便签上记一行」,记完贴上去就走,快得多。

2.2 启动流程:加载快照 → 重放流水账 → 安全模式

NameNode 重启时按三步恢复:

  1. 读入最新 FSImage,得到某个历史时刻的完整目录树
  2. 顺序重放 FSImage 之后的所有 EditLog 段,追平到最新状态
  3. 进入安全模式(SafeMode):只读、拒绝写操作,等待各 DataNode 上报块报告,直到汇报的块数达到阈值(dfs.safemode.threshold.pct 默认 0.999,即 99.9%)才自动退出

安全模式对新手比较陌生,打个比方:超市每天早上开门前先盘点货架,确认货都齐了才开始营业。NameNode 同理——它内存里的「块→DataNode 映射」重启后是空的(第 1.1 节),必须等 DataNode 们把各自手里的块清单报上来,确认「每个块都还有活着的副本」,才敢接受新的写请求,否则可能把新副本分配到已损坏的位置。

edits 段越长,启动时重放越久——这就是为什么必须定期 checkpoint(第 3 节),也是为什么监控 edits 段数量是 HDFS 运维的基本动作。

2.3 Checkpoint:把流水账「并账」进快照

Checkpoint(检查点)的本质是一次「并账」:

  1. 把当前 FSImage 与 EditLog 拉到检查点节点(SecondaryNameNode 或 HA 下的 Standby NameNode)
  2. 在内存中合并:以 FSImage 为底,重放 EditLog,得到最新快照
  3. 新 FSImage 传回 NameNode,NameNode 据此删除已合并的旧 edits 段

触发条件(参数为 dfs.namenode.checkpoint.*,Hadoop 2.x 起生效;1.x 时代名为 fs.checkpoint.*),满足其一即触发:

  • 距上次 checkpoint 超过 3600 秒(1 小时)(dfs.namenode.checkpoint.period)
  • 未合并的 edit 事务数达到 1,000,000 条(dfs.namenode.checkpoint.txns)
  • NameNode 每 60 秒检查一次上述条件(dfs.namenode.checkpoint.check.period)

以上默认值以 Apache Hadoop 官方文档为准,不同发行版(CDH/HDP 等)可能覆盖;拿不准的版本差异请查官方文档。

checkpoint 本身不提供高可用,它只保证 FSImage 不过期、NameNode 重启不用重放海量 edits——这是下一节要纠正的经典误解。

本节要点:元数据 = FSImage(总账)+ EditLog(流水),checkpoint 定期并账;NameNode 重启 = 读总账 + 重放流水 + 盘点等货齐(安全模式)。edits 越长,重启越慢。


3. SecondaryNameNode 的真正职责:Checkpoint 助手,不是热备

经典误解:SecondaryNameNode 是 NameNode 的热备,NameNode 挂了它立刻顶上。

错误。SecondaryNameNode(Hadoop 2.x 起官方改称 Checkpoint Node)的唯一职责就是每 60 秒检查一次,按条件触发 checkpoint——每 1 小时或未合并事务达 100 万条(第 2.3 节)。它既不持有集群最新状态,也从未被设计成可以接管服务。

可以把它想成会计助理:经理(NameNode)忙得不可开交,助理定期把总账和流水对一遍、并出新总账——但助理手里没有完整的最新账目,经理休长假时他不能代为签字。

误解 事实
是 NameNode 的热备,可无缝接管 它只做 checkpoint 合并;不持有最新元数据,无法接管
它「同步」了 NameNode 的所有状态 它定期拉取 FSImage + EditLog,拉取时刻之后的新操作它一无所知
有它就不用怕 NameNode 宕机 1.x 时代只能人工把它合并出的镜像拷回恢复,且必然丢失最后一段 edits;2.x 起的真正 HA 靠 QJM + ZKFC
它是 2.x 才有的组件 1.x 就有,职责完全相同;2.x 起官方文档改称 Checkpoint Node

为什么 HDFS 需要一个「专职并账」的组件? 因为 NameNode 若在服务期间自己做 checkpoint,要持锁合并大文件,会长时间阻塞写请求;把「并账」这种重活挪到一个廉价的旁路节点上,NameNode 保持高吞吐。这就是职责分工的意义。

2.x 之后的两个关键演进:

  • HA 集群(推荐路径):不再需要 SecondaryNameNode,改由 Standby NameNode 兼任 checkpoint——它本来就在通过 JournalNode 持续追平 edits,顺手并账零额外成本。这也是「SecondaryNameNode 这个角色正在退出历史舞台」的原因。
  • 非 HA 集群:继续用 SecondaryNameNode(Checkpoint Node),配置 dfs.namenode.checkpoint.dir 等参数即可。

真正的热备怎么做?——Active/Standby 双 NameNode + Quorum Journal Manager(QJM:奇数个 JournalNode,多数派写 edits)+ ZooKeeper Failover Controller(ZKFC)自动选主,详见 HDFS 高可用(QJM、ZKFC、Federation)。

本节要点:SecondaryNameNode ≠ 备份,它只是个「并账助理」。NameNode 宕机要无缝接管,只有 HA(QJM+ZKFC)一条路。


4. 心跳与块报告:节点「打卡」与「盘点」

HDFS 没有中心化的服务发现机制——NameNode 怎么知道集群里有多少节点、每个节点上有什么块?全靠 DataNode 主动上报的两类消息。

4.1 心跳(Heartbeat):每 3 秒一次的双向信道

DataNode 默认每 3 秒(dfs.heartbeat.interval)向 NameNode 发送一次心跳,报告自身存活与状态(剩余容量、正在服务的块数等)。就像员工每 3 秒向管理员「打卡」报平安。

但心跳不是单向的「报平安」,而是 NameNode 下发命令的载体:NameNode 在心跳应答里捎带指令——「把块 X 复制一份到节点 Y」「删除块 Z」「重新上报块报告」「执行 balancer 迁移」等。也就是说,NameNode 对集群的全部管控都搭在这条 3 秒一次的心跳通道上,没有额外的长连接管理通道。

如果 NameNode 连续约 10 分钟收不到某 DataNode 的心跳(受 dfs.namenode.heartbeat.recheck-interval 控制,默认 5 分钟),就判定该节点死亡(dead):将其标记下线,并对其身上「副本数不足」的块启动重新复制。注意这里是「约」——精确算法与版本相关,以 Apache Hadoop 官方文档为准。另外还有一个更轻的 stale(过期) 标记(dfs.namenode.stale.datanode.interval,默认 30 秒),只影响读写偏好,不等同于死亡。

为什么心跳间隔不能随便调大? 死亡判定时长 ≈ 2 × recheck 间隔 + 10 × 心跳间隔,默认约 630 秒(≈10 分钟)。3 秒改成 30 秒看似省了点网络流量,但故障发现会拖到约 900 秒(15 分钟),副本重建相应推迟,数据更长时间处于「副本不足」状态——这点「省下来的流量」与放大的故障风险完全不成比例。

4.2 块报告(Block Report):NameNode「库存台账」的唯一来源

  • 全量块报告:DataNode 启动时上报一次,之后默认每 6 小时(dfs.blockreport.intervalMsec = 21,600,000 毫秒)上报一次,内容是本节点所有块的清单(块 ID、长度、校验信息)。相当于每 6 小时做一次完整「货架盘点」。
  • 增量块报告(IBR, Incremental Block Report):两次全量报告之间,新增/删除/损坏的块通过增量报告实时告知 NameNode(搭乘心跳通道)。

块报告是 NameNode 内存中「块→DataNode 映射」的唯一数据来源(第 1.1 节)。NameNode 平时靠增量报告保持映射最新,全量报告主要用于启动后的重建与周期性对账——顺带发现 NameNode 不知道的孤儿块、修正损坏记录。

三种消息放在一起看:

消息 频率 携带内容 作用
心跳 每 3 秒 存活状态、容量、块数 判定节点死活 + 下发命令的通道
增量块报告 实时(有变化即报) 新增/删除/损坏的块 保持映射实时最新
全量块报告 启动时 + 每 6 小时 本节点全部块清单 启动重建映射 + 周期性对账纠错

4.3 一句话串起整条链路

DataNode 每 3 秒打卡(心跳,捎带命令)→ NameNode 判定存活;节点启动时和之后每 6 小时盘点货架(全量块报告),平时实时报增量 → NameNode 内存映射保持最新 → 某节点失联约 10 分钟 → 判定死亡 → 副本不足的块被重新复制。这一整条链路决定了 HDFS 的故障自愈速度,也决定了 NameNode 重启时的等待时长。

本节要点:心跳 = 3 秒打卡 + 命令通道;全量块报告 = 6 小时盘点,启动时重建 NameNode 的内存映射;约 10 分钟失联 = 判定死亡并启动副本重建。三个时间刻度(3 秒 / 6 小时 / 10 分钟)值得背下来。


5. 机架感知(Rack Awareness):让副本躲开同一根交换机

5.1 什么是机架感知

机架感知 = 让 HDFS 知道每台 DataNode 在哪个机架(rack)——物理上常指同一列机柜、同一交换机下的一组机器。配置方式(Hadoop 2.x/3.x):net.topology.script.file.name 指向一个拓扑脚本,脚本输入主机名、输出机架路径(如 /rack1、/rack2);未配置时所有节点统一归入默认机架 /default-rack。

对新手来说,「机架」可以直接理解为一栋楼:同一机架的机器摆在同一栋楼里,共享同一个总电闸、同一根网线出去。HDFS 眼里它们就是同一个「一荣俱荣、一损俱损」的集体。

5.2 为什么它重要:两个理由

理由一:副本跨机架 = 机架级容灾。 同一机架的机器共享同一电源/交换机,是 HDFS 眼中的同一个「故障域」。如果 3 个副本都落在同一机架,该机架断电 = 数据全部不可读。副本必须分散到不同机架(默认放置策略至少跨 2 个机架),机架断电时数据仍然可读。就像重要文件不能只放在一栋楼里——这栋楼停电烧了,另一栋楼还有备份。

graph TB
    subgraph rack1["机架 1(同一交换机 / 同一电源)"]
        DN1["DataNode-1<br/>块A 副本①"]
        DN2["DataNode-2<br/>块A 副本②"]
    end
    subgraph rack2["机架 2(另一个故障域)"]
        DN3["DataNode-3<br/>块A 副本③"]
    end
    NN["NameNode"]
    DN1 -. 块报告 .-> NN
    DN3 -. 块报告 .-> NN

上图:块 A 的 3 个副本跨 2 个机架。机架 1 整柜断电,机架 2 的副本③仍可读。

理由二:跨机架带宽是稀缺资源。 机架间流量要过核心交换机,比机架内流量昂贵得多。机架感知让副本放置尽量「就近」(第一副本放客户端所在节点、第二副本放同机架另一节点),把大部分读写流量锁在机架内部。

副本放置策略的完整规则(2.x 与 3.3+ 存在差异——3.3.0 起默认策略把写入时的跨机架流量从约 2/3 降到约 1/3)详见 HDFS 读写流程与副本放置策略,本文不展开。

常见坑:配了拓扑脚本但机架维度与实际机房不符,最终结果是数据分布与容灾假设脱节。机架感知是「配置一次、长期受益」的项,建议建集群时就做对——最简单的实现就是按 IP 段映射机架的脚本。

本节要点:机架 = 故障域(共享电源/交换机)。副本必须跨机架放,才能扛住「整柜断电」级别的故障;同时机架内流量比跨机架便宜,放置策略要两头兼顾。


6. 块大小 128MB:为什么 HDFS 用「大块」

dfs.blocksize 默认 134,217,728 字节 = 128MB。一个 1TB 的文件被切成约 8192 个块;块是存储、复制、计算调度(MapReduce 的 InputSplit 划分依据)的最小单元。

128MB 有多大?直观感受一下:约等于 6700 万个汉字(1 个汉字 2 字节)——一部《红楼梦》才约 73 万字,一个块能装约 90 部。传统文件系统的块是 4KB 级别,HDFS 大了三万多倍——为什么?

6.1 大块的三个收益

收益 解释 量化直觉
减少寻址(seek)开销 机械盘顺序读可达 100MB/s+,但每次寻道约需 10ms;块越大,顺序读占比越高 128MB 块每 GB 仅 8 次寻道;4MB 块每 GB 需 256 次
降低 NameNode 元数据压力 每个块在 NameNode 内存约占 150 字节(官方口径) 1TB 文件:128MB 块 = 8192 块 ≈ 1.2MB;若 1MB 块 = 约 100 万块 ≈ 150MB
减少对象与调度开销 块越少,块对象与 GC 压力越小,MapReduce 的 split 数量越少 百万级小 split 的调度开销远大于计算本身

「寻道」这个词,可以用老式唱片机来理解:磁头要移动到正确轨道上才能读(寻道,一次约 10ms),一旦开始顺序读,就像放唱片一样流畅(100MB/s+)。块越大,「翻面找轨道」的次数越少,纯放唱片的时间越多。

6.2 大块的代价(为什么不能无限大)

  • 小文件浪费:文件无论多小,至少占 1 个块(128MB 的逻辑配额)与 1 条 NameNode 元数据。100 万个 100KB 文件 = 100 万条元数据 + 逻辑上 128TB 的配额。小文件是 HDFS 最著名的坑,专门篇幅见 HDFS 小文件问题与治理。
  • 并行度受限:块是并行读写与任务切分的粒度,一个 8 块的文件最多 8 个任务并行读。对超大集群,很多生产环境直接调 256MB——块越大越省 NameNode 内存,但并行度进一步下降。

新手高频疑问:一个 1KB 的文件真的会占 128MB 磁盘吗? 不会——块是逻辑上的分配与调度单位,物理磁盘占用等于文件实际大小(加上少量元数据开销)。「至少占 1 个块」指的是逻辑配额与元数据条目,不是磁盘写零补齐。

关键结论:块大小是「NameNode 内存 + 寻址开销」与「并行度 + 小文件浪费」之间的权衡旋钮。可调的方向只有保持 128MB 或上调到 256MB(大规模集群),几乎没有任何理由调小。 想用「调小块」来缓解小文件问题是典型的错误方向——正确做法见系列小文件治理篇。

本节要点:大块 = 少寻道 + 省 NameNode 内存 + 少调度开销;代价 = 小文件浪费 + 并行度上限。128MB 是工程实践相对理想的配置。


7. 与传统 NAS / 分布式文件系统的对比

7.1 HDFS vs NAS(以 NFS 为代表)

先说清 NAS 是什么:一台(或一对)专用文件服务器挂上大容量存储,向网络中的机器提供文件服务,所有机器的读写请求都发给它。HDFS 则是前面讲的「元数据与数据分离、数据直连」的路子。

顺带解释一个绕不开的词:POSIX 是操作系统层面的「标准文件操作说明书」——open、read、write、随机改中间(seek)、删改任意字节,程序按这套规矩写就能跨平台工作。HDFS 为了大吞吐,主动放弃了这套说明书里的一部分能力。

维度 NAS(NFS) HDFS
访问模型 POSIX 全语义:随机读/写/改/追加 流式顺序读写;仅支持追加与截断,不支持随机写与就地修改
一致性 强一致(服务器单一视图) 写后读一致(read-after-write),足够满足分析场景
数据流 所有 IO 过文件服务器 控制流与数据流分离,数据在 DataNode 间直连
扩展性 服务器是瓶颈,横向扩展昂贵 廉价 x86 横向扩展,容量随节点数近似线性增长
定位 低延迟随机访问、通用文件语义 大文件、大吞吐、写一次读多次的批处理
单点风险 文件服务器(可双机但能力有限) NameNode 单点(依赖 HA 方案缓解)

「不支持就地修改」再打个比方:HDFS 上的文件像卷轴,只能在尾部接着写,不能翻开中间改几个字。所以它天生不适合频繁小改动的场景(如交易数据库的原始数据文件),却完美匹配「写一次、读多次」的分析型数据(日志、明细、归档)。

一句话:NAS 走的是「通用但贵、难横向扩展」的路,HDFS 走的是「为批处理大吞吐量身定制」的路——牺牲随机写与完整 POSIX 语义,换来单集群 PB 级容量与高吞吐。

7.2 与其他分布式文件系统的血缘与差异

  • GFS(Google File System):HDFS 的直接原型——GFS 论文 2003 年发表,HDFS 2006 年开源,架构几乎一一对应:Master=NameNode、ChunkServer=DataNode、64MB chunk=128MB 块。
  • Ceph:对象/块/文件三合一,POSIX 语义完整、去中心化(CRUSH 算法计算数据位置),适合需要通用语义与低时延的场景;吞吐能力同样强,但部署与调优复杂度高。
  • Lustre:面向 HPC(高性能计算)的 POSIX 并行文件系统,元数据与数据分层,面向 MPI 大规模并行顺序 IO(检查点/仿真输出)场景,但成本高、生态绑定紧。
对比项 HDFS Ceph Lustre
语义 流式,非 POSIX 类 POSIX(文件/块/对象统一) POSIX
强项 大数据生态原生、数据本地性、成本低 多协议统一、去中心化 HPC 高带宽、低时延
弱项 随机读写与小文件弱 部署运维复杂 成本高、生态绑定

补:Ceph 是什么(新手速览)。Ceph 是另一个开源分布式存储系统,一套集群提供三种接口:对象存储(像快递柜——按编号存取整件包裹,S3 兼容)、块存储(像给虚拟机挂虚拟硬盘)、文件存储(像共享文件夹)。它与 HDFS 最大的差异在「怎么找到数据」:

HDFS Ceph
找数据的方式 先问 NameNode「块在哪」再取(主从) 客户端用 CRUSH 算法自己算出位置(去中心化)
类比 问管理员拿货 查地图自己找货
单点 NameNode(靠 HA 缓解) 基本无
代价 元数据服务要精细供养 客户端逻辑复杂、部署调优难

组件对应关系:OSD ≈ DataNode(真正存数据)、MON(管集群状态与仲裁,HDFS 无对应)、MDS(只有文件接口才需要的元数据服务)。私有云常选 Ceph 做存储底座(OpenStack 的常用后端);Vertica Eon 的公共存储若用 S3 兼容对象存储,也可以落在 Ceph 上——本库的 S3 兼容存储实战见 某运营商 Vertica 数仓备份到日立 S3 对象存储报错问题处理报告。

HDFS 独有的杀手锏是「数据本地性」(data locality):计算框架(Spark/Flink/MapReduce)直接把任务调度到数据所在的节点,数据不用跨网络搬运——这是它与所有「共享存储型」系统最大的哲学差异,也是大数据生态以 HDFS 为存储底座的根源。

本节要点:HDFS 为「大文件、顺序读写、写一次读多次」而生,代价是放弃随机写。选型判断就一句话:你的负载像数据库(随机小改)还是像日志(批量大读)?


8. 组件职责总结表

组件 职责 故障影响
NameNode 维护命名空间元数据(FSImage + EditLog);提供文件→块→DataNode 定位服务;管控副本复制/删除 无 HA 时集群整体不可用;HA 下 ZKFC 数秒内完成主备切换
DataNode 块的实际存储与读写;心跳与块报告;执行 NameNode 指令 节点死亡后其上块暂时不可读(Client 自动切换副本),约 10 分钟后副本不足的块被重新复制
SecondaryNameNode / Checkpoint Node 按条件触发 checkpoint(每 1 小时或 100 万条事务)合并 FSImage 与 EditLog 挂掉只导致 checkpoint 暂停、edits 变长、NameNode 重启变慢;已落盘数据不丢失
Client 向 NameNode 请求元数据;直接与 DataNode 传输数据 不影响集群;单个客户端失败只影响其自身会话
JournalNode(QJM,HA 用) 持久化 edits 日志,多数派仲裁写入 少数派故障不影响;多数派故障时 NameNode 无法提交 edits(见 HDFS 高可用(QJM、ZKFC、Federation))

一句话记忆:NameNode 管账、DataNode 存货、Client 取货、SecondaryNameNode 并账、JournalNode 存账本。


9. 最佳实践清单

按投入产出比排序,每条附「为什么」:

  1. 副本数保持默认 3,并确认副本跨机架 —— 为什么:HDFS 容错的全部前提是副本落在独立故障域(机架/电源/交换机);副本数不足或同机架堆积,都会让「容忍 N 台机器故障」的承诺变成空话。
  2. 配置机架感知(net.topology.script.file.name),不要停留在 default-rack —— 为什么:不配脚本时 HDFS 认为所有节点同属一个机架,副本放置策略的跨机架逻辑完全失效,机架级故障会一次性打掉多个副本。
  3. 块大小保持 128MB(大规模集群可上调 256MB),绝不调小 —— 为什么:块是 NameNode 元数据与寻址开销的基本单位,调小 10 倍 = NameNode 内存需求放大 10 倍、读吞吐下降;用调小块解决小文件问题是错误方向。
  4. 监控 FSImage 大小、edits 段数量与 checkpoint 完成情况 —— 为什么:edits 无限增长直接拖慢 NameNode 重启(重放时间长)与故障恢复;checkpoint 卡住是 HDFS 运维中典型的暗雷。
  5. 不要把 SecondaryNameNode 当热备;HA 用 QJM + ZKFC —— 为什么:SNN 只做 checkpoint,人工接管必然丢最后一段 edits;2.x 起官方路径是双 NameNode + 奇数个 JournalNode + ZooKeeper 自动选主。
  6. 保持默认心跳(3 秒)与块报告间隔(6 小时) —— 为什么:心跳间隔决定故障发现延迟(默认约 10 分钟判定死亡),「省流量」的收益远小于副本重建推迟带来的数据风险。
  7. 为 NameNode 堆内存做元数据预算(经验估算约每 100 万文件/块需 1–2GB 堆) —— 为什么:NameNode OOM = 全集群不可用;文件/块总量是决定何时扩容、何时做 Federation 或迁移对象存储的分界依据。
  8. 小文件先聚合再入库(如 COPY 加载前先合并) —— 为什么:每个文件至少占 1 个块 + 1 条元数据,小文件同时打击 NameNode 内存与计算并行度,是 HDFS 性能的头号杀手。
  9. 大文件顺序读;随机小读请选其他系统 —— 为什么:128MB 大块与流式模型是为顺序吞吐设计的,随机小 IO 时延远差于 NAS 类系统,此类负载应交给 HBase(随机访问);海量小文件归档可考虑对象存储。
  10. Vertica 从 HDFS 加载时,数据块越大、分布越均匀,NameNode 交互越少 —— 为什么:Vertica 按块粒度向 NameNode 请求块位置(每读一个块发起一次请求,见 Vertica 读取 HDFS 文件的原理分析),块越少元数据交互越少;HDFS 侧参数调优见 Hadoop 调优之 HDFS 调优。

附录 A:术语速查表

术语 一句话解释 详见
HDFS Hadoop 分布式文件系统:把大文件切块撒到多台机器上 第 0 节
NameNode 主节点,只管元数据(文件→块→位置),不存数据 1.1
DataNode 从节点,真正存数据块的机器 1.2
Client 客户端,先问 NameNode 再直连 DataNode 传数据 1.3
Block(块) 存储基本单位,默认 128MB 第 6 节
Replica(副本) 同一块的多份拷贝(默认 3 份),跨机架存放 第 5 节
Metadata(元数据) 文件名、目录树、块位置等「描述数据的数据」 1.1
FSImage 命名空间某一时刻的完整快照(总账本) 第 2 节
EditLog 快照之后所有操作的日志(流水账) 第 2 节
Checkpoint 把 EditLog 合并进 FSImage 的「并账」过程 2.3
SafeMode(安全模式) NameNode 启动后盘点块状态、只读不写的阶段 2.2
SecondaryNameNode 只负责做 checkpoint 的旁路节点,不是热备 第 3 节
Active NameNode HA 集群中正在服务的 NameNode,处理全部读写请求 第 3 节
Standby NameNode HA 集群的热备,持续从 JournalNode 追平 edits,兼任 checkpoint;Active 故障时由 ZKFC 切换接管 第 3 节
Heartbeat(心跳) DataNode 每 3 秒向 NameNode 报平安 + 接收命令 4.1
Block Report(块报告) DataNode 汇报自己有哪些块(全量 6 小时 + 增量实时) 4.2
Rack Awareness(机架感知) 让 HDFS 知道节点在哪个机架,副本才能跨故障域 第 5 节
QJM / ZKFC HA 方案:JournalNode 多数派 + ZooKeeper 自动选主 第 3 节
Data Locality(数据本地性) 计算调度到数据所在节点,省跨网络搬运 7.2

附录 B:新手常见疑问(FAQ)

Q1:HDFS 能当普通网盘 / 文件服务器用吗? 不能。它不支持随机写、就地修改,小文件效率低,读时延明显高于 NAS 类系统——为批处理大文件设计,不是给「打开-编辑-保存」这类交互式场景用的。这类需求应选 NAS/对象存储。

Q2:文件存进 HDFS 后,在服务器硬盘上还能看到原文件吗? 看不到。文件被切成 blk_ 开头的块文件散落在各 DataNode 上,原始形态消失。你看到的目录树是 NameNode 用元数据「拼」出来的虚拟视图。

Q3:副本存 3 份是不是太浪费? 3 副本的代价是 200% 冗余,换来的是「任意 2 台机器同时挂,数据不丢」。对大数据场景,磁盘便宜、数据无价,这是公认的性价比之选。想省空间可以看 纠删码 vs 三副本存储策略 的 EC 方案(约 1.5 倍冗余)。

Q4:NameNode 就存点元数据,为什么会成为瓶颈? 因为「点元数据」乘以「亿级对象」就是大内存(150 字节 × 1 亿块 ≈ 15GB),且每个客户端每次读写都要先问它——它是所有请求的必经之路。所以 NameNode 要单独配好机器、管好内存预算(清单第 7 条)。

Q5:NameNode 挂了,集群会怎样? 无 HA:整个集群不可用(数据还在 DataNode 上,但没人指路了)。有 HA:ZKFC 数秒内把 Standby 切为 Active,客户端短暂重试后恢复。这也解释了为什么生产环境必须上 HDFS 高可用(QJM、ZKFC、Federation)。

Q6:为什么 NameNode 重启往往要几分钟? 启动 = 读 FSImage + 重放 edits + 安全模式等块报告。edits 越长、块越多,越慢。平时盯好 checkpoint(清单第 4 条)就是给重启「减肥」。

扩展阅读