HDFS(Hadoop分布式文件系统)的读写流程设计体现了其高容错、高吞吐量的特点。以下是核心流程解析:
一、HDFS 写流程(客户端写入数据)
1. 客户端发起请求
- 客户端调用
FileSystem.create()方法,通过HDFS Client向 NameNode 发起文件创建请求。 - NameNode 检查目标文件是否存在、客户端是否有权限,并记录文件元数据(不包含数据块位置)。
2. 数据管道建立
- NameNode 根据副本数(默认3)和机架感知策略,返回一组DataNode列表(如:DN1、DN2、DN3),形成写入管道(Pipeline)。
- 客户端将数据按块(Block,默认128MB)切分,写入本地缓存,然后通过
DFSOutputStream向管道写入。
3. 数据包传输
- 数据被分成多个Packet(64KB),依次发送到管道:
- 客户端发送 Packet 给第一个 DN1;
- DN1 接收后传给 DN2,同时保存到本地;
- DN2 同理传给 DN3,形成流水线复制。
- 每个 DataNode 写入成功后,会向上游返回ACK 确认。
4. 容错处理
- 若管道中某个 DataNode 失败:
- 管道关闭,剩余正常节点完成当前写入。
- NameNode 重新分配新节点,恢复管道继续写入。
- 已写入的块会被异步复制到满足副本数要求。
5. 完成写入
- 所有数据块写入完成后,客户端通知 NameNode提交文件,NameNode 更新元数据(记录块与 DataNode 的映射)。
- 若客户端不主动关闭,NameNode 不会认为文件已完成。
二、HDFS 读流程(客户端读取数据)
1. 客户端发起请求
- 客户端调用
FileSystem.open(),向 NameNode 请求文件元数据。 - NameNode 返回文件的所有块列表,以及每个块的副本所在 DataNode 地址(按网络拓扑排序,优先返回最近节点)。
2. 并行读取
- 客户端通过
DFSInputStream按顺序读取块:- 直接连接最近的 DataNode读取第一个块。
- 读取完成后,断开连接,再读取下一个块(可能从不同 DataNode)。
- 数据以Packet 为单位流式传输,客户端边收边处理。
3. 容错机制
- 若读取时遇到 DataNode 故障或数据损坏:
- 客户端尝试从该块的其他副本节点读取。
- 报告 NameNode 标记问题块,触发后续修复。
4. 关闭连接
- 所有块读取完成后,客户端关闭输入流。
三、核心设计要点
| 特性 | 写入流程 | 读取流程 |
|---|---|---|
| 数据流动 | 流水线复制(Pipeline) | 直接访问 DataNode |
| 网络优化 | 机架感知(就近写入) | 就近读取 |
| 容错 | 管道重建、副本恢复 | 自动切换副本 |
| 元数据交互 | 仅开始和结束联系 NameNode | 仅开始获取块位置 |
四、示例命令
# 写入文件hadoop fs -put localfile /hdfs/path/# 读取文件hadoop fs -get /hdfs/path/file localdir五、总结
- 写流程:通过数据管道实现高效副本写入,由客户端驱动,NameNode 只协调初始布局。
- 读流程:客户端直连 DataNode,NameNode 仅提供块位置,避免成为瓶颈。
- 关键思想:移动计算而非数据(读时就近)、一次写入多次读取(写流程优化为顺序写入)。