第
6 篇把客户端写路径追到 PrimaryLogPG 调用
queue_transactions()
交给对象存储后端。从这里开始,数据真正落到磁盘上的决定由
BlueStore
做出。本篇先把四个组件的职责边界划清,再讲写路径(第 8
篇)和读路径(第 9 篇)。
本文是「Ceph / RADOS 存储内核」系列第 7 篇(共 18 篇)。→ 系列目录
篇目 核心内容 第 6 篇 · 客户端写路径 librados → Primary → 副本;stale read 边界 第 7 篇 · BlueStore 架构 BlockDevice / BlueFS / RocksDB / Allocator 组件职责 第 8 篇 · BlueStore 写路径 min_alloc_size、deferred/WAL、checksum、压缩 上一篇:客户端写路径 · 下一篇:BlueStore 写路径
版本锚定:Ceph Squid v19.2.5;
docs.ceph.com/en/squid/rados/configuration/bluestore-config-ref/;docs.ceph.com/en/squid/dev/bluestore/;源码src/os/bluestore/@ v19.2.5。本篇无伪造基准测试数字。
一、FileStore 失败在哪里
在 BlueStore 之前,Ceph OSD 使用 FileStore 作为存储后端:把每个 RADOS 对象存成 XFS(或 ext4)上的一个文件,对象属性放 xattr,PG 元数据放目录项。这个设计的优点是可以复用成熟的内核文件系统,缺点则在大量对象场景下系统性地暴露。
学术谱系以 Aghayev 等人 SOSP 2019 的《File Systems Unfit as Distributed Storage Backends: Lessons from 10 Years of Ceph Evolution》为主锚点(下文简称 SOSP’19)。论文 Figure 2 给出后端演进时间线:EBOFS → FileStore(Btrfs/XFS)→ NewStore → BlueStore;BlueStore 在约两年开发后成为默认生产后端,2018 用户调查中约 70% 生产部署已采用(论文 Abstract / §1,口径为当时调查,非 Squid 现状)。本篇不编造本站未跑过的 FileStore/BlueStore 对比数字;凡涉及吞吐对比,只引用论文给出的 figure 与实验假设。
SOSP’19 把 FileStore 的失败归纳为三类可检验问题:
双写与事务阻抗(§3.1)。POSIX 文件系统无法直接提供 Ceph 所需的对象级原子更新,FileStore 在用户态实现逻辑 WAL:数据先完整写入 Journal,再应用到主文件系统。论文明确写到:数据被写两次,halving the disk bandwidth(§3.1.2 DoubleWrites)——这是机制陈述,不是本站实测。更细的一致性代价实验见 Figure 3:在同一套对象创建工作负载下,相对「裸盘 + 用户态」路径,XFS journaling 路径上对象创建吞吐约低 80%(4 TB HDD)/ 70%(400 GB NVMe)(论文假设:Seagate ST4000NM0023 / Intel P3600;单机对象存储微基准,非全集群客户端延迟)。
目录元数据与 splitting(§3.2)。RADOS 按
hash 枚举对象,FileStore
用深目录树承载百万级文件;readdir
慢且无序,触发 directory splitting
时会扫描并重建深层次目录。Figure 4:16 节点、约半数推荐 PG
数、128 并行、插入百万级 4 KiB 对象时,第二次 split 使全 SSD
集群吞吐跌落约 7 分钟(全 HDD 同文称约 120
分钟,图未展示)。这是「本地 FS
元数据策略与分布式对象命名冲突」的实证,不是笼统的「目录慢」。
新硬件接口(§3.3)。Host-Managed
SMR、ZNS 等与传统块接口不兼容;绑死在内核 FS
上会拖慢适配。BlueStore 把数据路径放到用户态裸设备,为后续
SMR/ZNS 实验留出空间(论文 §4.3;Squid 生产路径仍以
KernelDevice 为主)。
论文结论可以压成一句话:通用文件系统为 POSIX 应用设计,不为分布式对象存储设计——语义阻抗失配(semantic impedance mismatch)。BlueStore 切断该阻抗的方式是:数据直写裸盘、元数据进 RocksDB、RocksDB 跑在专用的 BlueFS 上。
BlueStore 从 Luminous(12.x)开始成为默认后端;在 Squid(v19.2.5)中,FileStore 已不在主线支持路径内。
二、架构总览
BlueStore 由四个职责严格分离的组件组成:
flowchart TB
upper["PrimaryLogPG / OSD Ops"] --> BS
subgraph BS ["BlueStore"]
direction TB
subgraph meta ["Metadata plane"]
rocksdb["RocksDB\nonode / blob / deferred WAL"]
bluefs["BlueFS\nRocksDB 文件系统"]
end
subgraph data ["Data plane"]
alloc["Allocator\nfree space bitmap / AVL"]
blockdev["BlockDevice\nraw block I/O (AIO / SPDK)"]
end
rocksdb --> bluefs
alloc --> blockdev
bluefs --> blockdev
end
BS --> blockdev
- BlockDevice:封装对裸块设备的异步 I/O;不理解对象语义。
- BlueFS:为 RocksDB 提供最小文件语义;只存 RocksDB 的 WAL 与 SSTable。
- RocksDB(通过 BlueRocksEnv):管理对象元数据(onode、blob、extent map)和延迟写 WAL。
- Allocator:在内存中维护裸盘空闲区间;写时分配,提交时持久化。
四者之间无循环依赖:数据面(BlockDevice +
Allocator)独立于元数据面(RocksDB + BlueFS),两条路径在
BlueStore::queue_transactions() 中汇合。
三、BlockDevice
BlockDevice(src/os/bluestore/BlockDevice.h
@ v19.2.5)是 BlueStore 对 I/O
介质的抽象接口。主要实现:
KernelDevice(src/os/bluestore/KernelDevice.cc):以
O_DIRECT | O_DSYNC 打开块设备,通过 Linux
AIO(libaio)异步提交读写;维护一个专属的
aio_thread 轮询 io_context_t
完成队列并回调。覆盖绝大多数生产场景:SATA / SAS HDD、SATA /
NVMe SSD。bluestore_aio_max_queue_depth
控制在途 aio 请求上限。
HMSMRDevice(src/os/bluestore/HMSMRDevice.cc):面向
Host-Managed SMR 磁盘,处理顺序写约束和分区管理。
NVMEDevice(可选,src/os/bluestore/NVMEDevice.cc):通过
SPDK 用户态驱动直接控制 NVMe,绕过内核块层;需编译时
--with-spdk。与 SPDK 系列
的用户态路径对接,但在 OSD 部署中由于独占 CPU core
进行轮询,运维代价较高,生产采用率不高。
BlockDevice
暴露的接口只有字节偏移和长度:aio_write(offset, length, bl, callback)、aio_read(offset, length, pbl, callback)、flush()。对象语义、校验、压缩均在
BlueStore 上层处理,BlockDevice 不感知。
四、BlueFS:为何不是「再挂一个通用 FS」
BlueFS(src/os/bluestore/BlueFS.h,BlueFS.cc
@ v19.2.5)是专为 RocksDB 设计的用户态、extent
型、带 journal 的最小文件系统。SOSP’19 Figure 5
把架构钉成:对象数据 → 裸盘 direct I/O;元数据 →
RocksDB;RocksDB → BlueFS → 同一(或分离的)裸盘。Figure 6
给出一种盘上布局:BlueFS 的元数据只活在 journal
里,journal extent 可与文件数据交错;WAL / LOG /
SST 是 RocksDB 生成的文件,不是 POSIX 目录树里的百万
inode。
4.1 与通用内核 FS 的分叉点
| 维度 | 通用 FS(XFS/ext4 等) | BlueFS |
|---|---|---|
| 服务对象 | 任意 POSIX 应用 | 几乎只服务 RocksDB 的 Env 调用面 |
| 接口 | 完整 POSIX + VFS | open / mkdir /
pwrite 等 RocksDB 所需子集;经
BlueRocksEnv |
| 元数据 | 持久 inode / 目录 B-tree | journal 中的 inode+extent 列表;mount 时装入内存 |
| 空闲空间 | FS 自己的分配器 | 不维护独立 freelist(论文 §4.1:coarse allocation,与 BlueStore Allocator 分工) |
| 崩溃恢复 | fsck / log replay 面向通用语义 | 重放自身 journal;文件数量通常为数十到数百量级 |
关键工程判断(可检验):若把 RocksDB 直接放在 XFS 上(NewStore 路线),仍要付 journaling FS 的一致性税,且对象数据仍是文件——SOSP’19 §3.1.3 说明这条路未能消除阻抗。BlueFS 的目标不是「更快的通用 FS」,而是让 LSM 的 WAL/SST 落在受控裸盘路径上,同时把对象数据完全移出 VFS。
与站内 RocksDB 内核系列 的边界:BlueFS 不改 LSM compaction 算法;它改的是 RocksDB 文件的承载介质与 fsync 路径。论文还提到将 RocksDB WAL 复用为环形缓冲以上游化一次 cache flush(§4.1)——那是 RocksDB 侧改动,本系列不展开。
4.2 多设备布局
BlueFS 支持把不同类型的 RocksDB
文件映射到不同物理设备(配置见
bluestore-config-ref):
| 文件类型 | 推荐设备 | 配置参数 |
|---|---|---|
| RocksDB WAL | 高速 NVMe | bluestore_block_wal_path |
| RocksDB SSTable | 容量 SSD 或 HDD | bluestore_block_db_path |
| 对象数据 | 主数据盘(HDD/SSD) | bluestore_block_path |
「三盘分离」(Data HDD + DB SSD + WAL NVMe)是典型高性能布局;单盘 NVMe 上亦可共置,由 BlueFS 在设备内划分区域。分离能降低 WAL 与数据写的带宽耦合,不能消除 DB 设备上 SST compaction 与元数据读的竞争——这正是第七节争论的入口。
五、RocksDB:对象元数据的存储引擎
BlueStore 把「文件系统会用 inode/目录表达的东西」压进
RocksDB 的有序键空间。前缀常量在
src/os/bluestore/BlueStore.cc(tag
v19.2.5 / 主线同源)中定义为:
| 前缀常量 | 字面量 | 内容 | 排障含义 |
|---|---|---|---|
PREFIX_SUPER |
S |
超级块字段 | 店面级元数据 |
PREFIX_STAT |
T |
统计 | 空间/计数类 |
PREFIX_COLL |
C |
collection → cnode | PG/集合 |
PREFIX_OBJ |
O |
onode | 对象元数据主路径;extent_map / attrs / size 在此 |
PREFIX_OMAP 等 |
M / P / m /
p |
omap(含 per-pool/per-pg 变体) | RGW 索引等大 omap 压力落在这里 |
PREFIX_DEFERRED |
L |
deferred 事务 | 小写/覆盖写的「未来 I/O 承诺」 |
PREFIX_ALLOC |
B |
freelist(offset→length) | Allocator 持久镜像;另有 b bitmap 变体 |
PREFIX_SHARED_BLOB |
X |
shared_blob_t | 跨对象 COW 共享 |
常见误读:blob 的物理描述与 csum
主要嵌在 onode
编码里,并不是单独一个「B =
blob」前缀——B 在源码里是 Allocator
freelist。Pacific 起还可把不同前缀分到多个 RocksDB
column family(bluestore_rocksdb_cf /
bluestore_rocksdb_cfs),以降低无关键的
compaction 耦合;升级自旧版的 OSD 可能仍未开启
sharding,需按文档核对。
5.1 onode 与 extent_map
每个 RADOS 对象对应 PREFIX_OBJ 下一条
onode。核心字段:
size:逻辑长度;attrs:xattr;extent_map:逻辑区间 → blob 的映射(读路径第 9 篇展开);- 共享 / flags:克隆 COW、压缩等。
extent_map 把逻辑地址空间切成区间;区间指向
blob,blob 再指向一个或多个 pextent(裸盘
(offset, length))。克隆通过
PREFIX_SHARED_BLOB(X)与引用计数共享物理区间——这是
SOSP’19 列出的 BlueStore 新颖点之一(§1 / §4:extent
reference-counting)。
5.2 blob 与 checksum 存放位置
bluestore_blob_t(bluestore_types.h)含:extents(pextent
列表)、csum_type /
csum_chunk_order /
csum_data、compressed/shared/has_unused
等 flags。
校验和随 blob 元数据进 RocksDB(通常随 onode 序列化),用户数据在裸盘。因此读路径可以在 onode 已缓存时只付一次数据 aio 的校验成本;写路径必须保证 kv 记录里的 csum 与最终落盘字节一致(第 8–9 篇)。FileStore/XFS 通常不对用户数据做端到端 checksum——这是 SOSP’19 强调 BlueStore「space-efficient … data checksums」的背景,而不是本站新测。
5.3 为什么是 RocksDB(以及边界)
工程动机可以钉成三条,均与论文/源码一致,而非「LSM 更现代」空话:
- 原子批量更新:onode +
deferred(
L)+ freelist(B)变更可进同一WriteBatch; - 与 Ceph 其他组件的运维同构(MON 等已用 RocksDB);
- deferred 小写把「数据承诺」塞进 KV,复用 WAL 崩溃语义(SOSP’19 §4.2)。
边界:本系列不重写 leveled compaction、stall、Column Family 算法细节;那些见 RocksDB 内核系列。本篇只钉 BlueStore 键命名空间 + BlueFS 承载,以及 compaction I/O 不经过 OSD mClock op queue(第 10 篇)这一控制面缺口。
六、Allocator:空闲空间管理
Allocator(src/os/bluestore/Allocator.h @
v19.2.5)在内存中维护裸盘空闲区间,粒度由
bluestore_min_alloc_size(创建 OSD 时由
bluestore_min_alloc_size_hdd /
_ssd 固化)决定。SOSP’19 写作时代的常见值为 HDD
64 KiB / SSD 16 KiB(论文 §4.2);Squid 文档默认已是
HDD/SSD 均为 4 KiB(Pacific 起 HDD 亦改为 4 KiB,见
bluestore-config-ref Minimum Allocation
Size)。创建后可用
ceph osd metadata osd.N | egrep 'rotational|alloc'
核对烘焙值——升级发行版不会改写已有 OSD
的该属性。论文还给出 Allocator 内存量级:约 35 MiB /
TiB 盘容量(§1
贡献列表;假设与实现随版本演变,Squid 以实际 allocator
为准)。BlueStore 提供多种实现,通过
bluestore_allocator 配置:
| 实现 | 键值 | 特点 |
|---|---|---|
BitmapAllocator |
bitmap |
以 min_alloc_size 为粒度的多级位图;内存紧凑 |
StupidAllocator |
stupid |
按大小分桶的区间集合;实现简单,历史较老 |
AvlAllocator |
avl |
AVL 树管理空闲区间;找最近匹配快 |
HybridAllocator |
hybrid |
大区间用 bitmap,小区间用 avl 子树 |
Squid v19.2.5 默认值请以
ceph config get osd bluestore_allocator
实际查询为准;社区在 Quincy/Reef/Squid
周期内多次调整默认值。
Allocator 生命周期:OSD 启动时从 RocksDB
的 PREFIX_ALLOC(B,及 bitmap 变体
b)重建内存 Allocator;这是 BlueStore mount
过程耗时的主要来源之一——当 OSD
管理数亿小对象时,重建可能需要数分钟,期间 OSD
不可服务,影响 PG peering 收敛。
为缓解这一问题,Ceph 引入了 allocator image 功能:在特定条件下将 Allocator 状态快照写入裸盘固定区域(绕过 RocksDB),重启时直接加载快照而不扫描 freelist。该功能的默认开启条件以 Squid 官方文档为准,不在所有部署场景下自动生效。
Allocator 与 RocksDB freelist 的一致性:写路径分配时只修改内存 Allocator;提交事务时同步将 freelist 变更写入 RocksDB。若 OSD 在提交前崩溃,已分配但未写入 RocksDB 的区间在下次 mount 时会被视为空闲重新加入 Allocator(因为 RocksDB 里没有对应的 onode 引用)。
七、学术谱系与工程分叉
谱系(可核对):
POSIX 本地 FS 作为分布式后端(常规智慧)
→ FileStore + 用户态 WAL(双写、目录 splitting;SOSP'19 §3)
→ NewStore(元数据进 RocksDB,数据仍在 FS;一致性税仍在)
→ BlueStore + BlueFS(数据裸盘 + KV 元数据;SOSP'19 §4,Figure 5)
→ 仍开放:RocksDB compaction vs 前台;Crimson/Seastore 是否替换整层
SOSP’19 的三个设计选择与失效点一一对应:RocksDB 批量原子更新替代逻辑 Journal 的全量数据记账、裸盘 direct I/O 去掉 journaling FS 双写路径、内存 Allocator + KV freelist 替代百万级目录 inode。论文实验口径(§6)是受控集群/微基准;把 Figure 3/4 的百分比直接当成「你的 Squid 集群一定快 X%」是错误外推——硬件代际、PG 数、是否共置 WAL/DB 都会改结论。
工程张力:嵌入 RocksDB
换来了事务与运维同构,也引入 compaction 突发 I/O。即便 WAL
在独立 NVMe 上,SST 合并仍可能打满 DB
盘或与共置数据争带宽。可检验信号:ceph tell osd.N perf dump
中 rocksdb compaction 计数上升时段,客户端写/读 P99
是否同向恶化(第 8–9 篇展开)。
争论(双方都有锚点):
- 保留并调优
RocksDB:生产默认路径;成本是
bluestore_rocksdb_options、分离 DB/WAL、观察 compaction——证据是 Squid 主线仍以此为默认。 - 替换存储引擎(Crimson /
Seastore):目标包含去掉 LSM compaction
对前台的非协作 I/O,以及削弱
BlueStore粗粒度锁。证据是社区公开设计陈述;不是已完成的普遍迁移。本系列第 18 篇回收该开放问题,本篇只要求读者把「compaction 不受 mClock 约束」记为控制面事实(第 10 篇)。
八、开放问题
1. RocksDB compaction 与 OSD 写 I/O
共争带宽。mClock 调度器(见第 10 篇)控制 OSD op
队列优先级,但不直接控制 RocksDB compaction 触发时机和 I/O
速率。高并发写场景下,compaction
会吃掉大量写带宽,直接表现为客户端写延迟毛刺。问题的可检验形式:ceph tell osd.N perf dump | grep rocksdb_compact
与前台 op 延迟分布的相关性。可读入口:Ceph 上游 tracker 中的
BlueStore compaction tuning 相关议题。
2. Allocator 重建延迟(Mount
Latency)。数亿小对象场景下,从 RocksDB
freelist(PREFIX_ALLOC)重建 Allocator
可能超过数分钟,导致 OSD 不可用时间拉长,影响 PG peering 后
acting set 收敛。allocator image 功能部分缓解,但在 Squid
中是否默认开启、在何种条件下生效,尚需运维验证。问题的可检验形式:OSD
日志中 store open latency 时间戳。
3. NVMEDevice(SPDK)的运维代价。SPDK
路径理论上消除内核块层延迟,但需要独占 CPU core
进行轮询,且与
iostat、nvme-cli、cgroup I/O
限速等工具链脱节。是否值得付出运维复杂度,取决于具体工作负载和团队能力,目前生产采用率有限。
九、参考资料
规范 / 官方文档(A 级)
- Ceph Squid,BlueStore Configuration
Reference,
docs.ceph.com/en/squid/rados/configuration/bluestore-config-ref/。 - Ceph Squid,BlueStore Internals(Developer
Guide),
docs.ceph.com/en/squid/dev/bluestore/。
源码(A 级)
ceph/cephtag v19.2.5:src/os/bluestore/BlueStore.cc(含PREFIX_*键前缀常量、_do_write_data()等);BlueStore.h、BlueFS.h、BlueFS.cc、Allocator.h、BitmapAllocator.cc、HybridAllocator.cc、BlockDevice.h、KernelDevice.cc、bluestore_types.h。
论文(A 级)
- Aghayev, A., Weil, S., Kuchnik, M., Nelson, M., Ganger, G. R., & Amvrosiadis, G. (2019). File Systems Unfit as Distributed Storage Backends: Lessons from 10 Years of Ceph Evolution. Proc. 27th ACM Symposium on Operating Systems Principles (SOSP ’19). 主锚点:§3 FileStore 失效(含 Figure 3 journaling 开销、Figure 4 directory splitting)、§4 BlueStore/BlueFS(Figure 5–6)、§4.2 deferred vs 大写直写。引用数字时保留原文硬件与工作负载假设,不外推为 Squid 生产保证。
站内
- 第 6 篇:客户端写路径 · 第 8 篇:BlueStore 写路径 · 系列目录
- RocksDB 内核系列(BlueFS/RocksDB 边界;本系列不重写 LSM 全书)
- SPDK 用户态存储栈(NVMEDevice 边界)
- Ceph 与 CRUSH(上游概览)
→ 系列目录 · 上一篇:客户端写路径 · 下一篇:BlueStore 写路径
读完这篇,下一步读什么
优先读同系列或同问题的下一篇,把单篇消费变成主题集群。
【Ceph RADOS】RADOS 内核全景:五轴坐标系与 18 篇路线
补齐站内 distributed/38 之上的 RADOS 内核层缺口;定义 Map 传播、通信、一致性、本地存储、接入代价五条坐标系,给出 18 篇阅读路线与系列边界。
【Ceph RADOS】BlueStore 写路径:min_alloc_size、deferred/WAL、checksum 与压缩
以 min_alloc_size 为分叉点拆解 BlueStore 的两条写路径:小写经 deferred WAL 先落 RocksDB 再后台应用到裸盘,大写直接落裸盘后原子更新 onode;说明 checksum 计算时机和压缩对分配逻辑的影响。
【Ceph RADOS】BlueStore 读路径:onode、blob、缓存与 checksum 失败模式
跟踪 BlueStore 一次读操作从 onode 缓存查询、extent map 展开、blob 物理位置定位,到 BlockDevice aio 读取和 checksum 验证的完整路径;梳理缓存架构与静默数据损坏的检测边界。
【Ceph RADOS】MON/MGR 与 Map:quorum、epoch 传播与客户端可见性
拆解 Ceph MON Paxos quorum、OSDMap/PGMap 结构与 epoch 传播机制;说明 MGR 主备模型与模块框架;分析客户端 epoch 缓存与可见性窗口的边界条件。