Skip to content

HDFS 分布式文件系统

什么是 HDFS

HDFS(Hadoop Distributed File System)是 Hadoop 的核心存储组件,基于 Google GFS 论文实现。它将大文件切分成固定大小的数据块(Block),分散存储在集群的多台机器上,并通过副本机制保证高可用。

核心设计目标

  • 存储超大文件(GB 到 TB 级别单文件)
  • 流式数据访问(一次写入,多次读取)
  • 运行在廉价商用硬件上
  • 高容错性(硬件故障是常态)

架构设计

核心组件

┌─────────────────────────────────────────────────────┐
│                    HDFS 集群                         │
│                                                     │
│  ┌──────────────┐        ┌──────────────────────┐  │
│  │  NameNode    │        │  Secondary NameNode   │  │
│  │  (主节点)    │◄──────►│  (辅助节点)           │  │
│  │  - 元数据    │        │  - 合并 EditLog       │  │
│  │  - 目录树    │        │  - 不是 HA 备份!     │  │
│  └──────┬───────┘        └──────────────────────┘  │
│         │                                           │
│    心跳 & 块报告                                     │
│         │                                           │
│  ┌──────▼──────────────────────────────────────┐   │
│  │              DataNode 集群                   │   │
│  │  ┌─────────┐  ┌─────────┐  ┌─────────┐     │   │
│  │  │DataNode1│  │DataNode2│  │DataNode3│     │   │
│  │  │ Block A │  │ Block A │  │ Block B │     │   │
│  │  │ Block B │  │ Block C │  │ Block C │     │   │
│  │  └─────────┘  └─────────┘  └─────────┘     │   │
│  └─────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────┘

NameNode(名称节点)

NameNode 是 HDFS 的大脑,负责管理文件系统元数据

  • 文件目录树结构
  • 文件与 Block 的映射关系
  • Block 与 DataNode 的映射关系(运行时维护,不持久化)

持久化机制

  • FsImage:文件系统元数据的快照(磁盘)
  • EditLog:对元数据的修改操作日志(磁盘)
  • 启动时:加载 FsImage + 回放 EditLog → 内存中的元数据

单点问题

NameNode 是单点,一旦宕机整个 HDFS 不可用。生产环境必须配置 HA(高可用),使用两个 NameNode(Active + Standby)。

DataNode(数据节点)

DataNode 负责实际的数据存储:

  • 存储数据块(Block)及其校验和(Checksum)
  • 定期向 NameNode 发送心跳(默认 3 秒)
  • 定期发送块报告(默认 6 小时),汇报本节点所有 Block 信息
  • 执行客户端的读写请求

Block(数据块)

HDFS 将文件切分成固定大小的 Block:

  • Hadoop 2.x 默认 128MB(Hadoop 1.x 是 64MB)
  • 每个 Block 默认保存 3 个副本(可配置)
  • 副本放置策略:第一副本本地,第二副本同机架不同节点,第三副本不同机架
文件 example.txt(300MB)

  ├── Block 0(0-128MB)  → DataNode1, DataNode2, DataNode4
  ├── Block 1(128-256MB)→ DataNode2, DataNode3, DataNode5
  └── Block 2(256-300MB)→ DataNode3, DataNode1, DataNode6

读写流程

写入流程

客户端

  1. 调用 DistributedFileSystem.create()

  2. NameNode 检查权限、文件是否存在,返回 DataNode 列表

  3. 客户端建立 Pipeline(流水线)
  │   Client → DN1 → DN2 → DN3

  4. 数据以 Packet(64KB)为单位写入 Pipeline
  │   每个 Packet 写完后,DN 逐级返回 ACK

  5. 文件关闭时,通知 NameNode 完成

关键点

  • 数据直接从客户端流向 DataNode,不经过 NameNode
  • Pipeline 写入:DN1 收到数据后立即转发给 DN2,不等全部写完

读取流程

客户端

  1. 调用 DistributedFileSystem.open()

  2. NameNode 返回文件的 Block 列表及对应 DataNode(按距离排序)

  3. 客户端直接从最近的 DataNode 读取每个 Block

  4. 读取完成,关闭流

HDFS HA 高可用

生产环境必须配置 HA,防止 NameNode 单点故障:

┌─────────────────────────────────────────────────────┐
│                  HDFS HA 架构                        │
│                                                     │
│  ┌──────────────┐        ┌──────────────────────┐  │
│  │  Active NN   │        │    Standby NN         │  │
│  │  (主 NameNode)│◄──────►│  (备 NameNode)        │  │
│  └──────┬───────┘        └──────────┬───────────┘  │
│         │                           │               │
│         └──────────┬────────────────┘               │
│                    │                                │
│         ┌──────────▼──────────┐                    │
│         │   JournalNode 集群   │                    │
│         │  (共享 EditLog)      │                    │
│         │  JN1  JN2  JN3      │                    │
│         └─────────────────────┘                    │
│                                                     │
│         ┌─────────────────────┐                    │
│         │   ZooKeeper 集群     │                    │
│         │  (自动故障切换)       │                    │
│         └─────────────────────┘                    │
└─────────────────────────────────────────────────────┘
  • JournalNode:Active NN 将 EditLog 写入 JournalNode 集群,Standby NN 实时同步
  • ZooKeeper:监控 NameNode 状态,Active 宕机时自动切换到 Standby

常用命令

bash
# 查看目录
hdfs dfs -ls /

# 上传文件
hdfs dfs -put localfile.txt /user/hadoop/

# 下载文件
hdfs dfs -get /user/hadoop/file.txt ./

# 创建目录
hdfs dfs -mkdir -p /user/hadoop/data

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

# 删除目录(递归)
hdfs dfs -rm -r /user/hadoop/data

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

# 查看文件大小
hdfs dfs -du -h /user/hadoop/

# 查看 Block 信息
hdfs fsck /user/hadoop/file.txt -files -blocks -locations

# 查看 NameNode 状态
hdfs dfsadmin -report

# 安全模式(启动时自动进入,等待 Block 报告)
hdfs dfsadmin -safemode get
hdfs dfsadmin -safemode leave

Java API 示例

java
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.*;

public class HdfsDemo {
    public static void main(String[] args) throws Exception {
        // 创建配置
        Configuration conf = new Configuration();
        conf.set("fs.defaultFS", "hdfs://namenode:9000");

        // 获取文件系统实例
        FileSystem fs = FileSystem.get(conf);

        // 上传文件
        Path src = new Path("/local/data.txt");
        Path dst = new Path("/hdfs/data.txt");
        fs.copyFromLocalFile(src, dst);

        // 读取文件
        FSDataInputStream in = fs.open(new Path("/hdfs/data.txt"));
        byte[] buffer = new byte[1024];
        int bytesRead;
        while ((bytesRead = in.read(buffer)) > 0) {
            System.out.write(buffer, 0, bytesRead);
        }
        in.close();

        // 列出目录
        FileStatus[] statuses = fs.listStatus(new Path("/hdfs/"));
        for (FileStatus status : statuses) {
            System.out.println(status.getPath() + " " + status.getLen());
        }

        // 删除文件
        fs.delete(new Path("/hdfs/data.txt"), false);

        fs.close();
    }
}

性能调优

小文件问题

HDFS 不适合存储大量小文件(每个文件在 NameNode 内存中占约 150 字节元数据):

解决方案

  1. HAR(Hadoop Archive):将小文件打包成 HAR 文件
  2. SequenceFile:将小文件合并成 SequenceFile(key=文件名,value=内容)
  3. CombineFileInputFormat:Spark/MapReduce 读取时合并小文件
  4. Hive 分区合并:定期合并小文件
bash
# 创建 HAR 文件
hadoop archive -archiveName data.har -p /input /output

# 查看 HAR 文件
hdfs dfs -ls har:///output/data.har/

数据倾斜

某些 DataNode 存储数据过多时,可以使用 Balancer 重新均衡:

bash
# 启动 Balancer(带宽限制 100MB/s)
hdfs balancer -threshold 10 -bandwidth 104857600

关键配置参数

xml
<!-- hdfs-site.xml -->

<!-- Block 大小(128MB) -->
<property>
  <name>dfs.blocksize</name>
  <value>134217728</value>
</property>

<!-- 副本数 -->
<property>
  <name>dfs.replication</name>
  <value>3</value>
</property>

<!-- NameNode 内存(生产环境建议 32-64GB) -->
<property>
  <name>dfs.namenode.handler.count</name>
  <value>100</value>
</property>

<!-- DataNode 数据目录(多磁盘) -->
<property>
  <name>dfs.datanode.data.dir</name>
  <value>/data1/hdfs,/data2/hdfs,/data3/hdfs</value>
</property>

常见问题排查

NameNode 进入安全模式

bash
# 查看安全模式状态
hdfs dfsadmin -safemode get

# 强制退出安全模式(谨慎使用)
hdfs dfsadmin -safemode leave

Block 丢失/损坏

bash
# 检查文件系统健康状态
hdfs fsck / -list-corruptfileblocks

# 删除损坏文件
hdfs fsck / -delete

DataNode 磁盘满

bash
# 查看各节点磁盘使用
hdfs dfsadmin -report

# 清理回收站
hdfs dfs -expunge

本站内容由 褚成志 整理编写,仅供学习参考