HDFS 深度解析:大数据存储的“硬盘不够、单盘坏了怎么办“

HDFS 深度解析:大数据存储的"硬盘不够、单盘坏了怎么办"

做大数据的人,绕不开 HDFS。

你的数据量上了 PB 级,单机硬盘装不下;硬盘总会坏,数据丢了怎么办?HDFS 就是 Google GFS 的开源实现,专门解决这两个问题:存储不够,加机器;硬盘坏了,多副本。

HDFS 解决的两个核心问题

问题1:硬盘不够用

单机硬盘容量有限(比如一块 20TB),但数据量可能是 PB 级。解决方案:分布式存储------多台机器组成集群,数据分散存到各个节点上。机器不够就加机器,理论上无限扩展。

问题2:硬盘坏了数据丢

单机硬盘会坏,坏了数据就没了。解决方案:多副本------每个数据块默认存 3 份,分别放到不同的机器上。一台机器挂了,数据还在。

这就是 HDFS 的核心理念:用廉价硬件的集群,解决海量数据的可靠存储问题。

HDFS 核心架构:主从协同的三层模型

HDFS 是典型的 Master-Slave 架构,四个角色各司其职:

角色 管什么 特点
NameNode 管"账本"(元数据) 一个集群只有一个,存内存里
DataNode 管"货"(实际数据) 每个节点一个,存磁盘上
Secondary NameNode 帮 NameNode 合并账本 不是备份,是助手
Client 用户入口 读写文件的接口

#mermaid-svg-PSYWoi6hTzImupr0{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-PSYWoi6hTzImupr0 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-PSYWoi6hTzImupr0 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-PSYWoi6hTzImupr0 .error-icon{fill:#552222;}#mermaid-svg-PSYWoi6hTzImupr0 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-PSYWoi6hTzImupr0 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-PSYWoi6hTzImupr0 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-PSYWoi6hTzImupr0 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-PSYWoi6hTzImupr0 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-PSYWoi6hTzImupr0 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-PSYWoi6hTzImupr0 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-PSYWoi6hTzImupr0 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-PSYWoi6hTzImupr0 .marker.cross{stroke:#333333;}#mermaid-svg-PSYWoi6hTzImupr0 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-PSYWoi6hTzImupr0 p{margin:0;}#mermaid-svg-PSYWoi6hTzImupr0 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-PSYWoi6hTzImupr0 .cluster-label text{fill:#333;}#mermaid-svg-PSYWoi6hTzImupr0 .cluster-label span{color:#333;}#mermaid-svg-PSYWoi6hTzImupr0 .cluster-label span p{background-color:transparent;}#mermaid-svg-PSYWoi6hTzImupr0 .label text,#mermaid-svg-PSYWoi6hTzImupr0 span{fill:#333;color:#333;}#mermaid-svg-PSYWoi6hTzImupr0 .node rect,#mermaid-svg-PSYWoi6hTzImupr0 .node circle,#mermaid-svg-PSYWoi6hTzImupr0 .node ellipse,#mermaid-svg-PSYWoi6hTzImupr0 .node polygon,#mermaid-svg-PSYWoi6hTzImupr0 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-PSYWoi6hTzImupr0 .rough-node .label text,#mermaid-svg-PSYWoi6hTzImupr0 .node .label text,#mermaid-svg-PSYWoi6hTzImupr0 .image-shape .label,#mermaid-svg-PSYWoi6hTzImupr0 .icon-shape .label{text-anchor:middle;}#mermaid-svg-PSYWoi6hTzImupr0 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-PSYWoi6hTzImupr0 .rough-node .label,#mermaid-svg-PSYWoi6hTzImupr0 .node .label,#mermaid-svg-PSYWoi6hTzImupr0 .image-shape .label,#mermaid-svg-PSYWoi6hTzImupr0 .icon-shape .label{text-align:center;}#mermaid-svg-PSYWoi6hTzImupr0 .node.clickable{cursor:pointer;}#mermaid-svg-PSYWoi6hTzImupr0 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-PSYWoi6hTzImupr0 .arrowheadPath{fill:#333333;}#mermaid-svg-PSYWoi6hTzImupr0 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-PSYWoi6hTzImupr0 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-PSYWoi6hTzImupr0 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-PSYWoi6hTzImupr0 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-PSYWoi6hTzImupr0 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-PSYWoi6hTzImupr0 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-PSYWoi6hTzImupr0 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-PSYWoi6hTzImupr0 .cluster text{fill:#333;}#mermaid-svg-PSYWoi6hTzImupr0 .cluster span{color:#333;}#mermaid-svg-PSYWoi6hTzImupr0 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-PSYWoi6hTzImupr0 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-PSYWoi6hTzImupr0 rect.text{fill:none;stroke-width:0;}#mermaid-svg-PSYWoi6hTzImupr0 .icon-shape,#mermaid-svg-PSYWoi6hTzImupr0 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-PSYWoi6hTzImupr0 .icon-shape p,#mermaid-svg-PSYWoi6hTzImupr0 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-PSYWoi6hTzImupr0 .icon-shape .label rect,#mermaid-svg-PSYWoi6hTzImupr0 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-PSYWoi6hTzImupr0 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-PSYWoi6hTzImupr0 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-PSYWoi6hTzImupr0 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 1. 元数据交互
2. 数据读写
2. 数据读写
2. 数据读写
3. 心跳/块报告
3. 心跳/块报告
3. 心跳/块报告
4. 元数据合并
存储数据
存储数据
存储数据
Client

客户端
NameNode

主节点 元数据管理
DataNode1
DataNode2
DataNode3
Secondary NameNode

辅助节点
本地磁盘

Block 副本
核心分工 NameNode 管元数据,DataNode 存数据,Client 直接读写数据节点

一句话总结:NameNode 管"什么东西存在哪",DataNode 管"东西本身",Secondary NameNode 帮 NameNode 整理账本。

NameNode:存的是"账本"不是"货"

NameNode 是 HDFS 的大脑,但它不存实际数据,只存元数据------文件在哪个目录、权限是什么、分成了几个块、每个块在哪些 DataNode 上。

元数据存在哪?两个文件:

  • FsImage:某一时刻的元数据快照(静态账本)
  • EditLog:操作日志(动态流水账),记录每次文件操作

启动的时候,NameNode 把 FsImage 读到内存,然后把 EditLog 里的操作重新执行一遍,生成最新的元数据。

NameNode 的瓶颈在哪? 所有元数据都在内存里。所以 HDFS 能存多少个文件,取决于 NameNode 的内存大小。每个文件/目录的元数据大约占 150 字节,如果内存是 128GB,理论上限大约是 8 亿个文件------但实际远到不了这个数。

小文件问题是真实存在的 :每个小文件都占一条元数据条目,还意味着大量的 Map 任务开销。能合并就合并。

DataNode:存的是"货",货在磁盘上

DataNode 是真正存数据的节点,实际数据以 Block 为单位存在磁盘上。

Block 大小:默认 128MB

  • 大 Block 适合大文件,减少寻址开销,提升吞吐
  • 小于 Block 的文件不会占满整个 Block(比如 1MB 文件只占 1MB)

DataNode 定期向 NameNode 汇报:

  • 心跳(Heartbeat):默认每 3 秒一次,告诉 NameNode "我还活着"
  • 块报告(Block Report):默认每 6 小时一次,告诉 NameNode "我手里有哪些 Block"

心跳停了,NameNode 就把这个节点标记为"死了",然后把上面的 Block 在其他节点上补副本。

Secondary NameNode:不是备份,是助手

这个命名容易误导人。Secondary NameNode 不是 NameNode 的热备,不能直接接管 NameNode 的工作。

它的职责是:定期帮 NameNode 合并 FsImage 和 EditLog,避免 EditLog 无限膨胀。

合并流程:

  1. Secondary NameNode 从 NameNode 拉取 FsImage 和 EditLog
  2. 在本地加载 FsImage,重放 EditLog 的操作,生成新的 FsImage
  3. 把新的 FsImage 传回 NameNode,替换旧的

如果 NameNode 挂了怎么办? 先用 Secondary NameNode 上的 FsImage 恢复元数据,但会丢失一部分最新的操作(EditLog 中尚未合并的部分)。这只能算"部分恢复",不能完全替代 NameNode。

客户端读写流程

读文件:

  1. Client 向 NameNode 请求文件位置 → NameNode 返回 Block 列表及所在 DataNode
  2. Client 直接连 DataNode 读数据(不经过 NameNode)

写文件:

  1. Client 把文件拆成 Block,向 NameNode 请求第一个 Block 的写入位置
  2. NameNode 返回一组 DataNode(用于存放副本)
  3. Client 以 pipeline 方式把数据依次发给第一个 DataNode,第一个再发给第二个,依次类推
  4. 写完确认后,向 NameNode 汇报元数据更新

关键点:NameNode 不参与数据传输,只负责协调位置。数据直接走 Client ↔ DataNode 通道。

HDFS 关键机制:确保高可用与高效读写

副本策略:3 份副本怎么放

默认 3 份副本,分布策略如下:

  1. 第一份:放在客户端所在节点(如果客户端不在集群内,随机选一个)
  2. 第二份:放在与第一份不同机架的节点
  3. 第三份:放在与第二份同机架但不同节点

为什么要跨机架? 一个机架的交换机坏了,整个机架的节点都断连。如果 3 个副本都在同一个机架,数据全丢。跨机架存储保证了即使单机架故障,数据仍然可用。

安全模式:NameNode 启动时的"保护期"

NameNode 启动时会进入安全模式(Safe Mode),此时:

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

如果集群刚启动时某个机架没上线,副本数不够,安全模式会一直卡住。这时候可以手动 hdfs dfsadmin -safemode leave 强制退出,但要确认数据完整性没问题。

小文件处理:优化 NameNode 内存占用

过多小文件会消耗 NameNode 内存并降低处理效率,解决方案包括:

  • 合并小文件 :通过 hadoop fs -getmerge 将多个小文件合并为大文件;
  • 使用 SequenceFile/ORC/Parquet:将小文件打包成列式存储格式,减少元数据量;
  • 限制小文件数量:在数据采集阶段(如 Flume)配置文件滚动策略,避免生成过多小文件。

HDFS 常见操作命令与最佳实践

常用命令示例
shell 复制代码
# 创建目录  
hdfs dfs -mkdir /user/hadoop/test  

# 上传本地文件到 HDFS  
hdfs dfs -put localfile.txt /user/hadoop/test/  

# 下载 HDFS 文件到本地  
hdfs dfs -get /user/hadoop/test/localfile.txt ./  

# 查看文件内容  
hdfs dfs -cat /user/hadoop/test/localfile.txt  

# 删除文件  
hdfs dfs -rm /user/hadoop/test/localfile.txt  

# 查看目录下文件信息(包括块大小、副本数)  
hdfs dfs -ls -h /user/hadoop/test  
相关推荐
青山木2 小时前
Redis 高可用的最后一公里:Cluster 分片、Gossip 与故障转移全流程
数据库·redis·后端·缓存
老孙讲技术2 小时前
巡店系统笔记:listDeviceDetailsByPage 台账 + bindDeviceLive 辅码流最小闭环
后端·物联网
Dong先生2 小时前
Harness vibecoding:从 PRD 到可交付代码的结构化工法
后端
雪隐3 小时前
用Flutter做背单词APP-03为了给单词库塞满数据,我让AI给我打了上万份工
前端·人工智能·后端
苏三说技术3 小时前
为什么越来越多的人使用PostgreSQL?
后端
Csvn3 小时前
📊 SQL 入门 Day 1:SELECT & WHERE — 数据查询的基本功
后端·sql
不一样的少年_4 小时前
不用框架,手搓 AI Agent:(一) 先让它跑起来
前端·后端·agent
用户526835677904 小时前
《拒绝当背锅侠!写了个 WebHook 中间件,让局域网硬件在服务挂掉时“喊”出来》
后端
云技纵横4 小时前
线上报 Deadlock,怎么从日志里找出是哪两条 SQL 打架?
后端