土法炼钢 · 系统与基础设施

【Ceph RADOS】BlueStore 架构:BlockDevice、BlueFS、RocksDB 与 Allocator

文章导航

分类入口
storagedistributed
标签入口
#ceph#bluestore#bluefs#rocksdb#allocator#blockdevice#squid#v19.2.5

目录

第 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.5docs.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 + Allocator)独立于元数据面(RocksDB + BlueFS),两条路径在 BlueStore::queue_transactions() 中汇合。


三、BlockDevice

BlockDevicesrc/os/bluestore/BlockDevice.h @ v19.2.5)是 BlueStore 对 I/O 介质的抽象接口。主要实现:

KernelDevicesrc/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 请求上限。

HMSMRDevicesrc/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.hBlueFS.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。核心字段:

extent_map 把逻辑地址空间切成区间;区间指向 blob,blob 再指向一个或多个 pextent(裸盘 (offset, length))。克隆通过 PREFIX_SHARED_BLOBX)与引用计数共享物理区间——这是 SOSP’19 列出的 BlueStore 新颖点之一(§1 / §4:extent reference-counting)。

5.2 blob 与 checksum 存放位置

bluestore_blob_tbluestore_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 更现代」空话:

  1. 原子批量更新:onode + deferred(L)+ freelist(B)变更可进同一 WriteBatch
  2. 与 Ceph 其他组件的运维同构(MON 等已用 RocksDB);
  3. 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_ALLOCB,及 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 篇展开)。

争论(双方都有锚点)


八、开放问题

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 进行轮询,且与 iostatnvme-cli、cgroup I/O 限速等工具链脱节。是否值得付出运维复杂度,取决于具体工作负载和团队能力,目前生产采用率有限。


九、参考资料

规范 / 官方文档(A 级)

源码(A 级)

论文(A 级)

站内


系列目录 · 上一篇:客户端写路径 · 下一篇:BlueStore 写路径

读完这篇,下一步读什么

优先读同系列或同问题的下一篇,把单篇消费变成主题集群。


By .