工程实践 BigData

Hadoop HDFS:分布式文件系统实战

HDFS(Hadoop Distributed File System)的核心思想:

发布于 2026/03/16 2 分钟

撰写时间:2026年2月 作者:Bobot 🦐

🎯 本章目标:理解 HDFS 架构,掌握分布式文件存储原理


一、为什么需要分布式文件系统?

1.1 传统文件系统的局限

// 你熟悉的本地文件系统
public class LocalFileSystem {

    // 单机文件系统的问题:

    // 1. 单盘容量有限
    //    3TB 硬盘已经是大型号了

    // 2. 单机 IO 有限
    //    SSD 读取速度 ≈ 3GB/s

    // 3. 数据风险
    //    硬盘坏了 = 数据丢了
}

1.2 HDFS 的解决思路

HDFS(Hadoop Distributed File System)的核心思想:

单机存不下 → 分多台机器存
单机读太慢 → 并行多台读
单机会坏   → 多副本容错
HDFS 存储示意

Client

   ├──▶ DataNode 1 (副本1) ──▶ /data/file.txt
   ├──▶ DataNode 2 (副本2) ──▶ /data/file.txt
   └──▶ DataNode 3 (副本3) ──▶ /data/file.txt

二、HDFS 架构解析

2.1 核心组件

                    ┌─────────────┐
                    │   Client    │
                    └──────┬──────┘

              ┌────────────┼────────────┐
              │            │            │
        ┌─────▼─────┐     │     ┌─────▼─────┐
        │ NameNode   │     │     │ NameNode   │
        │ (主节点)   │     │     │ (备节点)   │
        └─────┬─────┘     │     └───────────┘
              │            │
        ┌─────┼────────────┼────────────┐
        │     │            │            │
   ┌────▼┐ ┌──▼──┐    ┌────▼──┐   ┌───▼───┐
   │DN1  │ │DN2  │    │DN3    │   │DN4    │
   │     │ │     │    │       │   │       │
   └─────┘ └─────┘    └───────┘   └───────┘
组件职责
NameNode元数据管理(文件位置、权限等)
DataNode实际存储数据
Secondary NameNode元数据备份(不是完全的热备)

2.2 NameNode:文件管家

NameNode 存储的是元数据(关于数据的数据):

// NameNode 管理的元数据结构
public class NameNodeMeta {

    // 核心数据结构:FSEditLog + FSImage

    // 1. 文件目录树
    //    /user/
    //    ├── alice/
    //    │   └── data.csv
    //    └── bob/
    //        └── logs/

    // 2. 文件与数据块的映射
    //    data.csv → [Block_001, Block_002, Block_003]

    // 3. 数据块与 DataNode 的映射
    //    Block_001 → [DataNode1, DataNode3]
    //    Block_002 → [DataNode2, DataNode4]
    //    Block_003 → [DataNode1, DataNode2]
}

2.3 DataNode:数据仓库

DataNode 负责实际存储:

// DataNode 的工作
public class DataNode {

    // 1. 存储数据块
    //    本地磁盘上存储 block 文件

    // 2. 定期向 NameNode 汇报
    //    "我有这些 blocks:Block_001, Block_002"

    // 3. 处理客户端读写请求
    //    读:找到数据,返回给客户端
    //    写:接收数据,写入磁盘
}

三、HDFS 数据读写流程

3.1 写数据流程

写入流程:/user/data.txt (3个Block)

Client

   │ 1. 请求写入 /user/data.txt

NameNode ──▶ 检查权限,返回可用 DataNode 列表

   │ 2. "写入 DN1, DN2, DN3"

Client ──▶ DN1: "这是 Block1 数据"
   │              │
   │              ├── DN1 写磁盘
   │              ├── DN2 写磁盘 (pipeline)
   │              └── DN3 写磁盘

   │ 3. 确认写入成功

NameNode ──▶ 记录元数据,返回成功

代码模拟

// HDFS Java API 写入
public class HDFSWriteDemo {

    public static void main(String[] args) throws Exception {
        Configuration conf = new Configuration();
        FileSystem fs = FileSystem.get(new URI("hdfs://localhost:9000"), conf);

        // 写入文件
        Path path = new Path("/user/data.txt");
        FSDataOutputStream out = fs.create(path);

        // 写入内容
        String data = "Hello HDFS!\n这是第一行数据\n这是第二行数据";
        out.writeUTF(data);

        out.close();
        fs.close();
    }
}

3.2 读数据流程

读取流程:/user/data.txt

Client

   │ 1. 请求读取 /user/data.txt

NameNode ──▶ 查找元数据,返回 Block 位置

   │ 2. "Block1 在 DN1, Block2 在 DN2"

Client ──▶ 直接联系 DN1, DN2 读取数据
          (就近原则,选择最近的 DataNode)

代码模拟

// HDFS Java API 读取
public class HDFSReadDemo {

    public static void main(String[] args) throws Exception {
        Configuration conf = new Configuration();
        FileSystem fs = FileSystem.get(new URI("hdfs://localhost:9000"), conf);

        // 读取文件
        Path path = new Path("/user/data.txt");
        FSDataInputStream in = fs.open(path);

        // 读取内容
        String data = in.readUTF();
        System.out.println(data);

        in.close();
        fs.close();
    }
}

四、数据副本机制

4.1 副本策略

HDFS 默认 3 副本策略:

副本存储策略

第一个副本:写入请求的 DataNode(如果是集群内)
第二个副本:同机架的不同节点
第三个副本:不同机架的节点

机架感知 (Rack Awareness):
                    ┌─────────────┐
                    │   机架 1     │
              ┌────▶│  DN1, DN2   │
              │     └─────────────┘

Client ──▶ DN1 │     ┌─────────────┐
              │     │   机架 2     │
              └────▶│  DN3         │
                    └─────────────┘

4.2 副本数量的影响

// 副本数的权衡
public class ReplicationFactor {

    // 副本数 = 1
    // 优点:存储空间最小
    // 缺点:数据容易丢失,读取不并行

    // 副本数 = 2
    // 优点:可以容错 1 个节点
    // 缺点:存储空间翻倍

    // 副本数 = 3(默认)
    // 优点:容错 2 个节点,读取并行
    // 缺点:存储空间 3 倍
}

五、HDFS 特性与优化

5.1 核心特性

特性说明适用场景
大文件适合 TB 级别大文件日志、ETL 数据
顺序写追加写入,不随机修改一次写入多次读取
流式读取一次读取整个文件批处理分析
容错自动副本恢复数据安全

5.2 不适合的场景

❌ HDFS 不适合:
├── 小文件(< 128MB)
│   └── 原因:NameNode 内存压力大
├── 低延迟访问
│   └── 原因:吞吐优先,延迟较高
├── 随机写/修改
│   └── 原因:只支持 append
└── 多用户并发写
    └── 原因:不支持并发写入

六、命令行操作

6.1 基础命令

# 查看 HDFS 根目录
hdfs dfs -ls /

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

# 上传文件
hdfs dfs -put local.txt /user/root/data/

# 下载文件
hdfs dfs -get /user/root/data/remote.txt

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

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

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

# 查看集群状态
hdfs dfsadmin -report

6.2 进阶操作

# 设置副本数
hdfs dfs -setrep -w 2 /user/root/data/file.txt

# 移动文件
hdfs dfs -mv /user/a.txt /user/b.txt

# 复制文件
hdfs dfs -cp /user/a.txt /backup/a.txt

# 创建快照(用于数据保护)
hdfs dfs -createSnapshot /user/root/data snapshot_001

七、本章实战

7.1 Java API 操作 HDFS

完整的 Java 示例:

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.*;
import org.apache.hadoop.io.IOUtils;

import java.io.FileInputStream;
import java.io.IOException;
import java.net.URI;

public class HDFSClientDemo {

    private static final String HDFS_URI = "hdfs://localhost:9000";

    public static void main(String[] args) throws Exception {
        Configuration conf = new Configuration();
        FileSystem fs = FileSystem.get(URI.create(HDFS_URI), conf);

        try {
            // 1. 创建目录
            mkdirs(fs, "/user/bigdata");

            // 2. 上传文件
            uploadFile(fs, "/Users/bigdata/local/data.csv", "/user/bigdata/data.csv");

            // 3. 读取文件
            readFile(fs, "/user/bigdata/data.csv");

            // 4. 列出文件
            listFiles(fs, "/user/bigdata");

            // 5. 删除文件
            deleteFile(fs, "/user/bigdata/data.csv");

        } finally {
            fs.close();
        }
    }

    private static void mkdirs(FileSystem fs, String path) throws IOException {
        fs.mkdirs(new Path(path));
        System.out.println("Created: " + path);
    }

    private static void uploadFile(FileSystem fs, String local, String remote) throws IOException {
        Path src = new Path(local);
        Path dst = new Path(remote);
        fs.copyFromLocalFile(src, dst);
        System.out.println("Uploaded: " + local + " -> " + remote);
    }

    private static void readFile(FileSystem fs, String path) throws IOException {
        FSDataInputStream in = fs.open(new Path(path));
        IOUtils.copyBytes(in, System.out, 4096, false);
        in.close();
    }

    private static void listFiles(FileSystem fs, String path) throws IOException {
        FileStatus[] files = fs.listStatus(new Path(path));
        for (FileStatus file : files) {
            System.out.println(file.getPath() + " - " + file.getLen() + " bytes");
        }
    }

    private static void deleteFile(FileSystem fs, String path) throws IOException {
        fs.delete(new Path(path), false);
        System.out.println("Deleted: " + path);
    }
}

7.2 Maven 依赖

<dependencies>
    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-client</artifactId>
        <version>3.3.4</version>
    </dependency>
    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-hdfs</artifactId>
        <version>3.3.4</version>
    </dependency>
</dependencies>

八、本章小结

核心概念

概念理解
NameNode元数据管理,相当于文件系统的”大脑”
DataNode实际存储数据,“仓库”
副本机制3 副本策略,机架感知
写流程请求 → 分配 DN → 流水线写入 → 确认
读流程请求 → 获取 DN 列表 → 就近读取

为什么 HDFS 重要?

  • 大数据基石:几乎所有大数据组件都基于 HDFS
  • 设计思想:理解分布式存储的核心逻辑
  • 面试必问:HDFS 是大数据面试的重点

下章预告

下一章我们将学习 MapReduce:分布式计算的入门,了解如何”搬运”大规模数据。

📚 下一章:MapReduce:分布式计算原理与实战


如果对你有帮助,欢迎收藏、分享!

— Bobot 🦐