首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >一个问题,两张图看懂三大分布式文件系统:GFS、Ceph、3FS 的愿景、架构与技术取舍

一个问题,两张图看懂三大分布式文件系统:GFS、Ceph、3FS 的愿景、架构与技术取舍

作者头像
早起的鸟儿有虫吃
发布2025-10-10 13:15:33
发布2025-10-10 13:15:33
130
举报

为了回答:

请描述查找 /mnt/icfs/dir01/file.txt的过程。

从零开发分布式文件系统(四):一道经典面试题,深度对比 CephFS 与 3FS 的元数据架构优劣 写8千字解释

为啥 DeepSeek-3FS元数据无状态,CephFS 的 元数据 要搞得这么复杂? 3千字解释

还是解释不清楚,

  • 在压缩到500字
  • 一个问题,2个图
森林, 踪迹, 阳光, 森林路径, 小路, 树木, 早晨
森林, 踪迹, 阳光, 森林路径, 小路, 树木, 早晨

一、一个问题,2个图

面试官:看你项目经历有实现过文件系统,请描述查找 /mnt/icfs/dir01/file.txt的过程。

小王

感谢您的提问。 在描述具体过程之前, 我想使用一次反问权利来澄清一下背景信息,因为这会影响查找的细节。

请问, 这个文件系统的集群规模多大?存储了多少文件?是1个、1亿还是10亿个文件?

因为: 不同的产品有不同愿景, 不同愿景会有不同架构 不同不同架构会不同使用场景 面对同一个问题就会不同处理方式别想太多--只管去面

下面是是回答依据

unsetunset小王回答:GFS查找过程:unsetunset

假设你要读取 GFS 上的文件 /data/weblog.txt,起始位置是第 130 MB 处,流程如下:

  • 第一步(偏移 → chunk 索引): 客户端拿文件路径 /data/weblog.txt 和偏移量 130 MB,计算该偏移属于哪个 chunk。 每个 chunk 是 64 MB,所以:
    • chunk 0:0 – 64 MB
    • chunk 1:64 – 128 MB
    • chunk 2:128 – 192 MB 所以 130 MB 落在 chunk 索引 = 2(第 3 个 chunk,从 0 开始计数)。
  • 第二步(向 Master 查询元数据): 客户端向 Master 发送请求:“文件 /data/weblog.txt 的 chunk 索引 2 的 chunk handle 是什么?副本在哪里? Master 在内存中查命名空间 + 文件 → chunk 映射 + 副本列表,回应说:
    • chunk handle = xyz123
    • 副本在 ChunkServer A、ChunkServer C、ChunkServer F 上
  • 第三步(客户端选择副本 + 读取数据): 客户端选一个最优副本(例如 A)然后发请求: “给我 chunk handle = xyz123,从这个 chunk 内部偏移 2 MB 开始的数据。” (因为 130 MB − 128 MB = 2 MB) 然后 ChunkServer A 读取对应数据并返回给客户端
大街, 树木, 小路, 阳光, 此刻, 林地, 森林, 踪迹, 森林路径
大街, 树木, 小路, 阳光, 此刻, 林地, 森林, 踪迹, 森林路径

unsetunset小王回答: Ceph 文件查找过程unsetunset

  1. 起点:连接根目录

客户端首先连接到一个已知的 MDS。

  • 该 MDS 可能就是根目录 / 的权威 MDS,也可能不是。
  • 根目录的权威 MDS 通常是预先确定的 MDS0
  1. 逐级遍历路径

沿路径 /mnt/icfs/dir01/file.txt 逐级查找:

  1. 接收客户端请求的 接收 MDS 检查第一级目录 mnt 的权威 MDS。
  2. 如果 mnt 的权威 MDS 是自己,则直接查询本地元数据缓存。
  3. 如果不是,则将请求 转发 给正确的权威 MDS(例如 MDS-2)。
  4. MDS-2 解析其下目录 icfs 的目录项,并判断其权威 MDS。
  5. 逐级重复该过程,直到找到最终文件 file.txtinode

返回结果

  • 找到 inode 后,信息沿原路返回客户端。
  • 客户端获得 inode 后,直接与 OSD(对象存储守护进程)交互,完成文件数据的读写。

unsetunset3FS 文件访问流程unsetunset

1. 路径解析

  • 客户端根据文件路径(如 /mnt/icfs/dir01/file.txt)向 元数据服务(MDS) 请求文件信息。
  • MDS 解析路径,找到对应的 inode

2. inode → chunk 列表

  • inode 中记录了文件的逻辑数据块(chunk)列表。
  • 每个 chunk 对应一个 链(Chain),用于存储多个副本。

3. chunk → 链/链表

  • 每个 chunk 会被放到一个 链链表(Chain List) 上,链上包含多个存储目标(Storage Target)。
  • 链表可区分不同的业务场景,如在线服务或离线批处理。

4. 客户端访问链节点

  • 写操作:数据写入链头节点 → 沿链传播 → 链尾确认完成。
  • 读操作:客户端从链上任意节点读取数据 → 返回数据给客户端。

二、GFS

GFS
GFS

GFS

数据存储服务chunkserver
数据存储服务chunkserver

数据存储服务chunkserver

  • 每个 chunk 都会存上整整三份副本(replica)。其中一份是主数据(primary),两份是副数据(secondary)
  • GFS存储建立普通文件系统之上EXT4满足大文件存储,内部文件没有indoe概念只有全局编号
  • GFS不满足全部文件语义操作
元数据服务
元数据服务

元数据服务

  • 客户端会发出两部分信息,一个是文件名,另一个则是要读取哪一段数据,也就是读取文件的 offset 及 length 当做key ,value 数据存储位置
  • 类似块存储概念

unsetunset1.1 输入 文件路径+偏移量 返回什么?unsetunset

假设与前置设定

  • 假设 chunk 大小 = 64 MB(在 GFS 中常用值)
  • 假设文件路径 /data/logs/app.log,该文件在 Master 的元数据中对应若干 chunk
  • 假设这个文件在内部被分割成多个 chunk,chunk index 从 0 开始编号
  • 假设该文件最少有 3 个 chunk:chunk 0, chunk 1, chunk 2
  • 假设当前客户端请求的偏移量(byte offset)是 100 MB 处(即偏移量 = 100 * 2^20 字节 ≈ 104857600 字节)
  • 假设副本数 = 3(GFS 默认一般是 3 份副本)
  • 假设 Master 内存中有如下映射(示意):

文件路径

chunk index

chunk handle

副本位置(chunkservers)

/data/logs/app.log

0

H0

CS1, CS2, CS3

/data/logs/app.log

1

H1

CS2, CS4, CS5

/data/logs/app.log

2

H2

CS1, CS4, CS6

  • “CSx” 表示某个 chunkserver 节点(例如机器标识符)

输入

  • 文件路径/data/logs/app.log
  • 偏移量:100 MB (即字节偏移量大致 be 100 × 2^20 = 104,857,600 bytes)

计算 chunk index

  1. 客户端/GFS 客户端库会先把偏移量映射到文件的哪一个 chunk。
    • chunk 0:范围第 0 ~ 63,999,999 字节
    • chunk 1:范围第 64,000,000 ~ 127,999,999 字节
    • chunk 2:范围第 128,000,000 ~ 191,999,999 字节
  2. 100 MB 落在 chunk 1 的区间(因为 64 MB ≤ 100 MB < 128 MB) → 所以 chunk index = 1

客户端因此知道要访问这个文件的第 1 块。

向 Master 请求元数据

客户端向 Master 发送请求,请求内容可能包含:

  • 文件路径 /data/logs/app.log
  • chunk index = 1

Master 接收到这个请求后:

  • 在命名空间结构中确认 /data/logs/app.log 存在
  • 在内部的文件→chunk 映射表里查这条映射:对于该文件,第 1 块对应 chunk handle = H1
  • 查副本位置信息:该 chunk 的副本存在哪些 chunkservers?查询得出 CS2、CS4、CS5
  • Master 将这条响应返回给客户端

输出(Master 返回给客户端)

Master 的返回结果会包含:

  • chunk handle = H1
  • 副本列表 = [ CS2, CS4, CS5 ]

客户端选取副本 / 发起读取请求

客户端从副本列表 [ CS2, CS4, CS5 ] 中选择一个(例如离自己最近、网络延迟低、负载低的 CS4):

  • 客户端向 CS4 发送读取请求,内容包括:  - chunk handle = H1  - 偏移量在该 chunk 内部的偏

unsetunset1.2 GFS 没有文件inode每个如何表示tree结构unsetunset

GFS的元数据与“树”结构

虽然GFS没有inode,但它同样需要管理文件和目录,形成树状结构。

  • 元数据的三大支柱:Master节点主要管理三类元信息:
  1. 命名空间(Namespace):这就是GFS的“目录树”,它是一个逻辑结构,记录了所有的文件和目录路径,例如 /data/weblog.txt。
  2. 文件到块的映射:记录每个文件具体由哪些数据块(Chunk)组成。Master会为每个Chunk分配一个全局唯一的64位标识符,称为 Chunk Handle。
  3. 块的位置信息:记录每个Chunk副本存储在哪些ChunkServer上。值得注意的是,这部分信息Master并不持久化存储,而是通过ChunkServer定期上报的心跳信息来动态获取和更新。

unsetunset1.3 GFS使用场景unsetunset

测试类型

客户端数量

测得吞吐 / 速率

读(单客户端)

1

~ 10 MB/s

读(16 客户端并发)

16

~ 94 MB/s

写(单客户端)

1

~ 6.3 MB/s

写(16 客户端并发)

16

~ 35 MB/s

追加写 (record append),单客户端

1

~ 6.0 MB/s

追加写 (record append),16 客户端

16

~ 4.8 MB/s

  • GFS论文是2003年的数据
    • 当时典型机器是 1 GHz CPU、几十 MB/s 磁盘带宽。
    • 网络一般是 100 Mbps 或 1 Gbps,不是现代高速 100G。
  • 表格里的吞吐量(10 MB/s 单客户端读)在当时已经算不错了。
  • 所以绝对值与现代硬件带宽不可比,不是设计瓶颈,而是时代限制

GFS 的愿景

  • 用廉价的商用服务器组成集群,处理 PB 级别 / 大规模数据。
  • 重点是 高吞吐量 和 _容错性_。例如,顺序读写要很快,节点故障要能自动恢复,副本机制容忍硬件故障。

GFS 的适用场景

  • 大数据分析、日志存储、搜索索引构建。这类场景写入很多、追加写入常见、顺序读取多。
  • 批量处理任务比交互式任务多。例如 MapReduce 作业、数据聚合、批日志处理等。

GFS 的局限性 /取舍

  • 随机读写性能较差:对小文件频繁读取、修改会导致效率低,因为每次访问都要请求 Master 获取元数据,且 Chunk 大小较大,浪费 I/O。
  • 小文件开销高:很多元数据(命名空间、文件→Chunk 映射、权限等)都要由 Master 管理。小文件多会占大量元数据,Master 内存压力、管理复杂性都增大。
  • POSIX 语义支持不完全:GFS 不严格支持所有 POSIX 特性(例如文件锁定、原子 rename、读写一致性等方面有放宽)
  • Master 的单点:Master 节点集中管理元数据,是潜在瓶颈 /单点故障(尽管设计中有日志 /重启 /副本机制,但原始 GFS 中 Master 是关键且风险点) [

二、Ceph

unsetunset2.1 先看一组数据unsetunset

节点数量与性能
节点数量与性能

节点数量与性能

延迟与吞吐量
延迟与吞吐量

延迟与吞吐量

  • Ceph:愿景是 统一存储系统(块、文件、对象),
  • 强调 可扩展、高可用、分布式一致性。 这里并没有性能
  • 去中心化元数据管理(CRUSH 算法定位对象),MDS 只负责目录树。

MDS节点个数超过128个,单节点性能急速下降,性能延迟增加

  • 小集群(1-8 节点)
    • MDS 吞吐非常高,尤其是 openssh+include(>4000 ops/s),opensharedopenssh+lib 也在 2000-3000 ops/s。
    • 单节点 MDS 可承载大量操作,因为并发冲突和分布式协调开销很小。
  • 中等规模(16-64 节点)
    • 吞吐开始下降,每个 MDS 处理的 ops/s 逐渐降低。
    • 说明随着 MDS 集群增大,跨节点协调、锁争用、元数据分布开销增加
  • 大集群(64-128 节点)
    • openssh+include 保持最高吞吐
    • makefiles 最低(<1000 ops/s),说明创建小文件操作对 MDS 压力大。
    • 每个 MDS 吞吐继续下降到 ~500-2500 ops/s 不等。
    • 大规模集群下,负载分散,单 MDS 吞吐下降,但整体集群总吞吐可能仍高
    • 不同操作差异明显:

1️⃣ 愿景

  • 大规模、可扩展、高可靠统一存储(对象、块、文件)。
  • 去中心化,自动平衡和故障恢复。

2️⃣ 架构

  • RADOS:核心对象存储,CRUSH 算法决定数据分布。
  • OSD:存储数据和副本。
  • MON:集群状态监控。
  • MDS:CephFS 的元数据管理。
  • RBD / CephFS:块存储 / 文件系统接口。

3️⃣ 使用场景

  • 云平台 VM 虚拟磁盘
  • 大数据 / AI 数据存储
  • 企业文件共享 / NAS
  • 长期备份与归档

4️⃣ 技术选择亮点

  • 存储方式:对象为主,块和文件为接口
  • 可靠性:副本或纠删码
  • 性能优化:BlueStore + 客户端直连 OSD
  • 元数据管理:RocksDB + MDS 单线程

unsetunset2.2 元数据节点单线,无锁冲突,为什么性能还是这么慢unsetunset

问:ceph元数据单节点性能为么这么慢?不是因为单线慢了,更维护一致性

回答:ceph 设计目标可扩展,非低延迟,

底层数据对象存储(块存储),通过crush算法完全去中心化,

然后增加文件系统这个功能后,底层数据对象存储根本不负责解决这个事情,

不提供kv查询

元数据节点承担 维护目录树,一致性责任

例如节点重启时候, 为了减少对存储池查询, 通过不同元数据节点协商 动态加载整个目录树, 分布式锁 缓存一致性

mds状态
mds状态

mds状态

为什么慢

  • 文件元数据比 KV 重:路径解析、权限校验、目录更新、link/count、目录遍历等多步逻辑,远比简单的 get/set 多 I/O 与 CPU 步骤。
  • 元数据写入要多次同步:修改通常要写 RocksDB/WAL、同步到 BlueStore/RADOS,产生写放大和 fsync 延迟。
  • 后端延迟:RocksDB compaction、磁盘(尤其是非 NVMe)延迟、网络往返都会拉长一次元数据操作的时延。
  • 大量小文件/随机操作:每个操作都得走 MDS,无法批处理,容易把 MDS 压垮。
  • 热点不均衡:少数热目录会集中在同一个 MDS 上,造成单点饱和。
  • 客户端缓存/lease 失效:缓存未命中或需要回退/验证,会增加额外交互。
  • 跨 MDS 协调成本:当目录被拆分到多个 MDS 时,跨子树操作需要分布式协调/锁,短期内会变慢。
  • 后台任务影响:compaction、rebalancing、GC、快照合并等会占用 I/O/CPU,抬高延迟。

2.3 架构原因

三. 3FS(Fire-Flyer File System)

unsetunset3.1 测试数据unsetunset

指标

测试 / 说明

峰值聚合读取吞吐

6.6 TiB/s(在 180 节点、500+ 客户端的读压测中)

厂方/媒体宣称峰值

~7.3 TB/s(厂方/媒体峰值宣称)

GraySort 排序性能

3.66 TiB/min(排序 110.5 TiB 数据用时 30 分 14 秒)

KV cache 峰值读带宽

40 GiB/s(KV 缓存工作负载峰值)

实测(云环境)

在 AWS 上用 SoftRCoE + 大规格实例做 FIO,可把实例带宽压满,验证在无 IB 的云环境也能获得高吞吐

第三方复核 / 现实检验

指出“性能高度依赖硬件/网络/测试方法”,建议做现实性核查(瓶颈归因)

unsetunset3.2 关键点unsetunset

unsetunset🔹 技术愿景unsetunset

  • 面向 AI 训练与推理、高性能计算(HPC)场景。
  • 提供 高吞吐、低延迟、可扩展 的分布式文件系统。
  • 支持海量小文件与大文件,兼顾强一致性与文件系统语义。

unsetunset🏗 架构设计unsetunset

核心组件(全部通过 RDMA 互连):

  1. Cluster Manager(集群管理器)
    • 管理集群成员、配置分发、主备切换。
    • 配置存储在可靠 KV 存储(如 FoundationDB/ZooKeeper/etcd)。
  2. Metadata Service(元数据服务)
    • 处理文件系统操作:open、create、rename 等。
    • 无状态,元数据存储在事务性 KV 存储(FoundationDB)。
  3. Storage Service(存储服务)
    • 管理本地 SSD,提供 chunk 存储。
    • 使用 Chain Replication with Apportioned Queries (CRAQ) 实现强一致性。
    • 文件拆分成 chunk,跨多 SSD 链式复制。
  4. Client(客户端)
    • FUSE 客户端:易用,兼容多数应用。
    • Native 客户端:支持异步零拷贝 I/O,提高性能。
    • 客户端可直接计算 chunk ID 与链表,减少元数据依赖。

unsetunset⚙️ 技术选型unsetunset

  • 网络:RDMA(InfiniBand / RoCE)低延迟高带宽。
  • 存储:本地 SSD,链式复制,支持高并发访问。
  • 元数据管理:FoundationDB,事务性 KV 存储,Serializable Snapshot Isolation。
  • 文件系统接口
    • 支持原子目录操作、符号/硬链接。
    • 保持 POSIX 文件接口,便于现有应用迁移。
  • 优化
    • 异步零拷贝 API(类似 io_uring)。
    • Chunk 物理存储分配池 + Copy-on-write 元数据更新。
    • 大文件 stripe,按目录可配置链表、chunk/stripe 尺寸。
    • 恢复过程最小化对正常 I/O 的干扰。
    • FUSE 对小随机读性能有限,native 客户端解决。

unsetunset数据放置与复制unsetunset

  • Chunk 存储
    • 文件分块,跨多个链复制。
    • 写入:head → tail;读取:任意 replica。
    • CRAQ 协议优化读密集型 workload。

链式复制 (Chain Replication) 与 CRAQ

这是一种强一致性的复制协议。

链式复制(Chain Replication)

写入只能将请求发送至head节点,写入流程:

  • 写入路径(head → tail): 所有写入请求都必须发送到链的头部(Head),然后由头部依次同步到链中的下一个副本,直到传递到尾部(Tail)。尾部确认后,才代表整个写入成功。这确保了所有副本都以相同的顺序接收数据,保证了强一致性
  • 读取路径(任意副本): 读取请求可以发送到链上的任何一个副本。这极大地扩展了读取吞吐量,因为读流量可以被均匀分摊到所有副本上,而不用像主从复制那样只从主副本读取。
  • CRAQ优化: 这是对基础链式复制的改进。它允许链上所有副本都处理读请求,但副本之间通过版本号等机制来协调,确保读取到的总是已提交的最新数据,从而在保持强一致性的同时获得了极高的读性能。
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-09-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 后端开发成长指南 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、一个问题,2个图
    • unsetunset小王回答:GFS查找过程:unsetunset
    • unsetunset小王回答: Ceph 文件查找过程unsetunset
    • unsetunset3FS 文件访问流程unsetunset
      • 1. 路径解析
      • 2. inode → chunk 列表
      • 3. chunk → 链/链表
      • 4. 客户端访问链节点
  • 二、GFS
    • unsetunset1.1 输入 文件路径+偏移量 返回什么?unsetunset
      • 假设与前置设定
      • 输入
      • 计算 chunk index
      • 向 Master 请求元数据
      • 输出(Master 返回给客户端)
      • 客户端选取副本 / 发起读取请求
    • unsetunset1.2 GFS 没有文件inode每个如何表示tree结构unsetunset
      • GFS的元数据与“树”结构
    • unsetunset1.3 GFS使用场景unsetunset
  • 二、Ceph
    • unsetunset2.1 先看一组数据unsetunset
    • unsetunset2.2 元数据节点单线,无锁冲突,为什么性能还是这么慢unsetunset
      • 2.3 架构原因
  • 三. 3FS(Fire-Flyer File System)
    • unsetunset3.1 测试数据unsetunset
    • unsetunset3.2 关键点unsetunset
    • unsetunset🔹 技术愿景unsetunset
    • unsetunset🏗 架构设计unsetunset
    • unsetunset⚙️ 技术选型unsetunset
    • unsetunset数据放置与复制unsetunset
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档