一、HDFS简介
HDFS(Hadoop Distributed File System)是一个分布式文件系统,它的主要设计目标是为了解决存储和处理大规模数据的挑战,尤其针对低成本硬件集群和高吞吐量批处理场景。其有以下几个主要特性:
跨平台
(底层由Java开发,天然支持跨平台部署)高容错
(数据冗余存储,数据块默认有3个副本)高吞吐
(并行读取或写入多个数据块,且是顺序读写,流式数据访问)横向拓展
(NameNode只管理元数据,实际数据由DataNode分布式存储)
但HDFS也有一些局限性,比如以下几个点
- 小文件性能差(小文件如果非常多,其元数据会占用大量空间)
- 不支持文件修改(只能顺序追加)
所以HDFS更适用于大规模数据存储以及离线批处理场景
二、系统架构
HDFS采用的是Master/Slave主从架构
,由一个NameNode和多个DataNode节点组成,架构示意图如下:
NameNode(管理节点):是HDFS的重要组成部分,管理整个HDFS集群,主要有以下几个作用:
- 元数据管理:维护文件命名空间和数据块之间的映射关系(比如/logs/a.log这个文件,需要维护这个文件分为哪几个数据块,每个数据块在哪个DataNode节点上,对应的副本又在哪个DataNode节点上)
- 客户端协调:客户端读写文件时,都会先与NameNode节点进行通信,拿到协调结果后再跟实际的DataNode进行通信(后面会详细介绍客户端读写过程)
- DataNode故障处理:DataNode定时向NameNode发送心跳,如果检测心跳超时,则标记DataNode失效,触发副本复制,保持副本数量一致
DataNode(数据节点):是实际存储数据
的节点,主要有以下几个作用
- 数据块存储:每个文件都会被切分成一个个数据块Block(默认128M),而DataNode负责实际存储这些数据块及其副本(比如数据块副本数为3,则3个DataNode节点上各存储一个该数据块的副本)
- 心跳与状态汇报:DataNode每隔一定的周期(默认3秒)向NameNode发送心跳,并且定期向NameNode汇报本地存储的数据块列表
- 数据读写操作:与客户端进行交互,响应客户端与其他DataNode的数据块读写请求
SecondaryNameNode(SNN):NameNode的辅助,定期地将edits文件合并到fsimage文件(这个操作叫checkpoint)
,避免edits文件过大,影响NameNode启动恢复时间
三、NameNode持久化设计
NameNode会将元数据全量存储到内存中,这样可以快速响应客户端请求,但内存并不会永久保留,所以NameNode对元数据进行了持久化设计(快照+操作日志,与Zookeeper设计思想基本一致
),保证元数据的持久化。持久化的关键在于两个文件,fsimage快照文件 和 edits操作日志文件,存放的路径在NameNode的 ${dfs.namenode.name.dir}/current 目录下,如下图
edits操作日志
edits文件是增量操作日志文件,会记录所有对元数据的修改操作(比如创建/删除文件、修改权限等)。
生成机制:NameNode会实时将元数据操作追加到edits文件中
,并更新内存中的元数据,并且当edits文件达到阈值(默认64M),滚动生成一个新的edits文件
fsimage快照文件
fsimage文件是某一时刻HDFS的元数据全量快照文件,记录了完整目录结构、文件权限、数据块分配信息等。所以当前HDFS的全量元数据 = 最新fsimage + edits
。
生成机制:SecondaryNameNode会定期(默认1小时)将fsimage和edits日志合并,生成新的fsimage快照
快照合并步骤:
- SecondaryNameNode 请求NameNode停止使用当前edits文件,也就是上图里圈红的edits_inprogress文件,生成新edits.new继续记录操作
- SecondaryNameNode下载fsimage和旧edits文件到本地,
将fsimage文件反序列化到内存中,并重放旧edits文件,得到全量元数据
- 将内存中的全量元数据序列化生成fsimage文件,并上传给NameNode
启动恢复
NameNode加载最新的fsimage快照到内存中,并重放edits_inprogress文件,将元数据更新到最新状态(与上面SecondaryNameNode合并过程中的操作是一致的)
四、客户端读写流程
客户端读取文件
- 客户端向NameNode请求文件的元数据(比如数据块位置,副本信息)
- NameNode返回文件对应的数据块列表Block IDs以及每个数据块分别对应哪个DataNode
- 客户端拿到数据块信息,与对应的DataNode建立连接,并请求数据块
- DataNode返回数据块
- 客户端验证数据块的CRC32校验和,保证数据完整性,并按逻辑顺序将多个数据块拼接为完整文件
- 关闭与DataNode的连接
客户端写入文件
- 客户端向NameNode发起文件写入请求
- NameNode为文件第一个数据块分配写入通道(假设有个DataNode节点,副本为3,那么通道可以是DN1 -> DN3 -> DN5)
- 客户端与DN1建立连接
- 开始链式传输第一个数据块,传输链路:客户端 -> DN1 -> DN3 -> DN5(客户端不直接与DN3、DN5建立连接)
- 每个DataNode接受完数据块之后,会向上一个DataNode节点返回ACK,直到DN1返回ACK给客户端
- 客户端向NameNode提交该数据块,表示该数据块已完成写入
- 重复2-6步骤,提交所有数据块后,通知NameNode整个文件已完成写入
- NameNode更新该文件的元数据信息
五、总结
HDFS作为Hadoop生态的存储基石,提供了统一、可靠、可拓展的底层存储,支持多种数据格式(如文本、parquet、orc等),与MapReduce、Hive、Spark、Flink等计算框架深度融合
,最近几年lakehouse(湖仓一体)架构开始慢慢走进大家的视野,HDFS或对象存储也可以作为数据湖的底层存储,所以HDFS在大数据领域扮演着非常重要的角色。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。