- 存储
- 分布式文件系统
- 对象存储
- 后端
- 高可用
【免费下载链接】ceph
Ceph is a distributed object, block, and file storage platform
CephFS(Ceph File System)将文件数据与元数据彻底分离:所有文件数据都以 RADOS 对象的形式存储于存储集群,而目录、文件名、权限等元数据由 MDS(Metadata Server)负责维护。本文以 cephfs-io-path.rst 为核心,系统讲解 CephFS 的数据 I/O 路径——客户端如何通过"能力(capability)"协商获得文件读写权、如何直接绕过 MDS 访问 RADOS 完成数据读写、数据对象如何按<inode号>.<对象索引>分条命名,以及用户态(libcephfs + librados)与内核态(ceph.ko + libceph.ko)两条客户端路径的异同。读完本文,你将掌握 CephFS 数据平面与元数据平面分离的设计精髓,理解读缓存(cache)与写缓冲(buffer)能力的语义,并能从源码级理解文件布局参数(stripe unit / stripe count / object size)如何决定对象的切分方式。
一、总览:元数据与数据分离的 I/O 架构
CephFS 的核心设计原则是:文件数据全部存放在 RADOS 对象中,客户端可以直接访问 RADOS 读写文件数据,MDS 只处理元数据操作。这一设计使得数据路径上没有 MDS 这个中间环节,从而避免了元数据服务器成为数据吞吐的瓶颈。
doc/cephfs/cephfs-io-path.rst中给出的架构图清晰地展示了这条两条路径:
+---------------------+ | Application | +---------------------+ | V +---------------------+ Data I/Os +--------------------+ | CephFS Library | ---------> | LibRados | +---------------------+ +--------------------+ | | | Metadata Operations | Objects Read/Write V V +---------------------+ +--------------------+ | MDSs | -=-------> | OSDs | +---------------------+ +--------------------+ +----------------------+ +---------------------+ | CephFS kernel client | Data I/Os | Ceph kernel library | | (ceph.ko) | --------> | (libceph.ko) | +----------------------+ +---------------------+ | | | Metadata Operations | Objects Read/Write v v +---------------------+ +--------------------+ | MDSs | -=-------> | OSDs | +---------------------+ +--------------------+图中上、下两半分别对应 CephFS 的两种客户端实现:
- 用户态客户端:应用 → CephFS Library(libcephfs)→ LibRados → OSDs;元数据操作走 CephFS Library → MDSs。
- 内核态客户端:应用 → CephFS kernel client(ceph.ko)→ Ceph kernel library(libceph.ko)→ OSDs;元数据操作走 ceph.ko → MDSs。
两条路径的数据 I/O 都直接发生在客户端与 OSD 之间,MDS 只负责元数据(目录层次、inode 分配、能力协商、锁管理等)。这也是 CephFS 能够在大量客户端并发读写时保持扩展性的根本原因——数据平面是水平扩展的(OSD 集群),元数据平面由 MDS 集群承载,二者互不干扰。
二、能力(Capability)协商:读/写文件前的第一道关卡
在 CephFS 中,读写一个文件的前提是客户端拥有对应 inode 的 'file read/write' 能力(capability)。能力机制是 CephFS 保证多客户端缓存一致性的核心协议,其实现遍布 MDS 与客户端两侧:
- MDS 侧:
src/mds/Capability.h/src/mds/Capability.cc定义能力对象,src/mds/Locker.cc负责能力的发放、收回与锁协调; - 客户端侧:
src/client/Client.cc维护客户端视图中的能力集合,并在 open 文件、需要扩展能力时向 MDS 发起请求。
2.1 能力不足时怎么办:cap message
当客户端没有所需的文件读写能力时,它不会直接去访问 RADOS,而是先向 MDS 发送一个cap message,告诉 MDS 自己想要什么。MDS 在条件允许时会向客户端发放(issue)能力。只有拿到 'file read/write' 能力之后,客户端才被允许直接访问 RADOS 读写文件数据。
这一协商流程保证了:任何时刻客户端对某 inode 的读写权限都由 MDS 统一裁决,多个客户端之间的缓存一致性(例如一个客户端要独占写、其他客户端必须 flush 并丢弃缓存)都由 MDS 通过能力回收与再发放来维护。
2.2 能力位的源码定义
能力的底层定义位于 src/include/ceph_fs.h,每种通用能力位(generic bits)都通过左移叠加到对应的能力域(auth、link、xattr、file)上:
#define CEPH_CAP_GSHARED 1 /* client can reads */ #define CEPH_CAP_GEXCL 2 /* client can read and update */ #define CEPH_CAP_GCACHE 4 /* (file) client can cache reads */ #define CEPH_CAP_GRD 8 /* (file) client can read */ #define CEPH_CAP_GWR 16 /* (file) client can write */ #define CEPH_CAP_GBUFFER 32 /* (file) client can buffer writes */ #define CEPH_CAP_GWREXTEND 64 /* (file) client can extend EOF */对应到文件域(file realm)的能力位定义如下:
#define CEPH_CAP_FILE_SHARED (CEPH_CAP_GSHARED << CEPH_CAP_SFILE) #define CEPH_CAP_FILE_EXCL (CEPH_CAP_GEXCL << CEPH_CAP_SFILE) #define CEPH_CAP_FILE_CACHE (CEPH_CAP_GCACHE << CEPH_CAP_SFILE) #define CEPH_CAP_FILE_RD (CEPH_CAP_GRD << CEPH_CAP_SFILE) #define CEPH_CAP_FILE_WR (CEPH_CAP_GWR << CEPH_CAP_SFILE) #define CEPH_CAP_FILE_BUFFER (CEPH_CAP_GBUFFER << CEPH_CAP_SFILE) #define CEPH_CAP_FILE_WREXTEND (CEPH_CAP_GWREXTEND << CEPH_CAP_SFILE)这些位进一步组合成客户端常用到的能力集合(同一文件的相邻代码段):
CEPH_CAP_ANY_SHARED:任意域的 shared 能力(多个客户端可以同时持有,代表"我可以读");CEPH_CAP_ANY_EXCL:任意域的 excl 能力(代表"我可以独占读写并更新");CEPH_CAP_ANY_FILE_RD = CEPH_CAP_FILE_RD | CEPH_CAP_FILE_CACHE | ...:文件读能力全集;CEPH_CAP_ANY_FILE_WR = CEPH_CAP_FILE_WR | CEPH_CAP_FILE_BUFFER | ...:文件写能力全集;CEPH_CAP_ANY:所有能力全集。
注意区分:CEPH_CAP_FILE_RD(可以读 RADOS 对象)与CEPH_CAP_FILE_CACHE(可以把读到的数据缓存到本地,后续读直接命中缓存)是两回事;CEPH_CAP_FILE_WR(可以写 RADOS 对象)与CEPH_CAP_FILE_BUFFER(可以把写操作缓冲在本地缓存,稍后异步 flush)也是两回事。这正是原文档强调的能力语义。
2.3 单客户端场景:cache 与 buffer 能力
原文档特别指出:如果文件只被一个客户端打开,MDS 还会向这个唯一的客户端发放 'file cache/buffer' 能力。二者的含义是:
- file cache 能力:文件读可以由客户端缓存满足(读命中本地缓存即可返回,无需每次都访问 OSD);
- file buffer 能力:文件写可以被缓冲在客户端缓存中(写操作先在本地缓存聚合,再由客户端按对象异步刷写到 RADOS)。
在多客户端场景下,由于缓存一致性难以保证,MDS 不会轻易发放 cache/buffer 能力;而在单客户端(或 MDS 判定可以安全共享)的场景下,cache/buffer 能力让客户端获得类似本地文件系统的读写性能,同时依旧保持数据最终落在 RADOS 对象上的持久性。这也是 CephFS 在单客户端工作负载(如单机挂载运行数据库)下表现出色的重要原因之一。
三、数据分条(Data Striping):<inode号>.<对象索引>的对象命名与布局
客户端获得文件读写能力后,便直接访问 RADOS 进行数据读写。文件数据以<inode number>.<object index>的形式存储为 RADOS 对象。也就是说,每个 CephFS 文件对应一系列 RADOS 对象,对象名由文件的 inode 号与对象序号拼接而成。
关于数据分条的完整背景,原文档指引读者参考 Architecture 文档的 Data Striping 一节。该节阐述了分条的必要性与原理:
- 存储设备存在吞吐量上限,因此存储系统通常把连续的数据分条(striping)到多个设备上以提高吞吐与性能(类似 RAID 0);
- Ceph 的分条能够同时获得 RAID 0 的吞吐、n 路镜像的可靠性以及更快的恢复速度;
- 注意:Ceph 存储集群中的对象本身是不分条的,分条逻辑由各客户端负责——CephFS、RBD、RGW 都会把各自数据分条到多个存储集群对象上;直接通过 librados 写数据的客户端必须自行完成分条与并行 I/O 才能获得这些收益。
最简单的分条形式是 stripe count = 1:客户端把一个文件的 stripe unit 顺序写入对象,直到对象达到最大容量,再创建下一个对象继续写入。对于大文件、大对象,客户端把 stripe unit 并行写入对象集合中的多个对象时,写入性能提升显著——因为不同对象经 CRUSH 映射到不同的 placement group 与不同的 OSD,写操作可以并行发生、叠加多块盘的吞吐。
3.1 文件布局(file layout)的源码结构
决定一个文件如何切分为对象的参数统称为"文件布局(file layout)",其内核协议结构定义在 src/include/ceph_fs.h:
__le32 fl_stripe_unit; /* stripe unit, in bytes. must be multiple ... */ __le32 fl_stripe_count; /* over this many objects */ __le32 fl_object_size; /* until objects are this big, then move to ... */ __le32 fl_object_stripe_unit; /* UNUSED. for per-object parity, if any */三个核心参数的含义:
| 参数 | 含义 | 说明 |
|---|---|---|
stripe_unit | 分条单元(字节) | 写入每个对象的数据块大小,必须是某个对齐单位的倍数 |
stripe_count | 分条对象数 | 在一个对象集合(object set)内轮转多少个对象 |
object_size | 对象大小(字节) | 对象达到该大小后,开始使用下一批对象 |
当 stripe_count > 1 时,客户端按 stripe_unit 大小把文件逻辑区间轮流映射到 stripe_count 个对象上,形成对象集合;stripe_count 个对象写满 object_size 后,再开启下一个对象集合,对象索引继续递增。最终文件在 RADOS 中的对象序列正是<inode>.<n>、<inode>.<n+1>、<inode>.<n+2>……
3.2 布局的查看与设置
CephFS 布局可以按目录继承式地设置。常用操作:
# 查看文件/目录的布局 getfattr -n ceph.file.layout /mnt/cephfs/somefile # 为目录设置布局(新文件将继承该布局) setfattr -n ceph.file.layout.stripe_unit -v 1048576 /mnt/cephfs/bigdir setfattr -n ceph.file.layout.stripe_count -v 4 /mnt/cephfs/bigdir setfattr -n ceph.file.layout.object_size -v 4194304 /mnt/cephfs/bigdir # 或用 ceph fs 命令查看文件系统默认布局 ceph fs get cephfs在 cephfs-journal-tool.rst 中可以见到一个真实的布局 JSON 示例:
"layout": { "stripe_unit": 4194304, "stripe_count": 1, "object_size": 4194304 }这是一个典型的 4 MiB 对象、单对象分条布局。更完整的布局机制(继承规则、pool 选择、约束条件)参见 file-layouts.rst;而布局与权限的结合点——只有拥有p标志(layout-modification)的客户端才能修改布局,参见 client-auth.rst。
3.3 分条与副本的关系
一个容易混淆的点是:分条与对象副本是相互独立的。CRUSH 算法负责把对象副本分布到多个 OSD 上(例如 size=3 的三副本),而分条决定一个文件切分成多少个逻辑对象。因此,一个文件既可能被分条成多个对象(跨盘并行),每个对象又拥有多份副本(可靠性),二者叠加而不冲突。
四、两条客户端实现路径的纵深对比
原文档用同一幅图描绘了两条数据通路,二者的数据平面本质相同,但实现位置不同:
| 维度 | 用户态客户端(libcephfs) | 内核态客户端(ceph.ko) |
|---|---|---|
| 数据 I/O 库 | librados(用户态) | libceph.ko(内核态) |
| 元数据协议 | libcephfs 通过 Messenger 与 MDS 通信 | ceph.ko 通过内核消息与 MDS 通信 |
| 能力管理 | src/client/Client.cc中的 Cap 跟踪 | 内核ceph文件系统驱动的 capability 结构 |
| 适用场景 | 需要灵活性的应用、ceph-fuse 挂载 | 高吞吐的内核 VFS 挂载(mount -t ceph) |
无论哪条路径,"文件数据 = RADOS 对象、数据直连 OSD、元数据走 MDS"的架构都不变。MDS 侧对两种客户端一视同仁地执行能力协商与锁管理(src/mds/Locker.cc),因为能力协议是线缆级(wire protocol)的公共协议,而不是某个客户端的私有实现。
五、总结与延伸阅读
CephFS 的 I/O 路径可以概括为一条主线:能力协商(cap message → MDS 发放能力)→ 客户端直连 RADOS → 按文件布局把数据分条为<inode>.<对象索引>对象 → 并行读写 OSD。MDS 全程只参与元数据与能力仲裁,不参与数据搬运;单客户端场景下 cache/buffer 能力还能让客户端获得本地缓存读与写缓冲的性能收益。
如果希望继续深入,推荐按以下顺序阅读仓库中的相关材料:
- doc/cephfs/cephfs-io-path.rst:本文核心依据;
- doc/architecture/ceph-protocol.rst:数据分条的原理与图示;
- doc/cephfs/file-layouts.rst:布局参数的完整语义与继承规则;
- src/include/ceph_fs.h:能力位与文件布局结构的协议定义;
- src/mds/Locker.cc 与 src/mds/Capability.h:MDS 侧能力发放与锁协调的实现;
- src/client/Client.cc:用户态客户端侧能力跟踪与数据读写逻辑。
- 存储
- 分布式文件系统
- 对象存储
- 后端
- 高可用
【免费下载链接】ceph
Ceph is a distributed object, block, and file storage platform
相关推荐
Nacos 客户端能力协商(Ability Negotiation)机制深度解析:gRPC 连接建立期的能力协商规范与源码实现
Nacos 客户端能力协商(Ability Negotiation)机制深度解析:gRPC 连接建立期的能力协商规范与源码实现 导读 本文围绕 Nacos 的《
后端微服务配置中心服务注册发现云原生Karukan上下文工程实战:10个字符的lctx为什么这么够用
Karukan上下文工程实战:10个字符的lctx为什么这么够用 Karukan 是一个面向 Linux 与 macOS 的开源日语输入法,核心是一台神经网络假
人工智能NLP本地部署桌面应用listmonk数据库读写分离客户端配置:连接路由
listmonk数据库读写分离客户端配置:连接路由 你是否在使用listmonk管理大规模邮件列表时遇到数据库性能瓶颈?当订阅用户超过10万、日发送量突破百万时
后端企业应用
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考