RocksDB在Ceph中的调用与实现分析
目录
- 文档目标
- 整体定位
- 核心代码位置
- 抽象层KeyValueDB
- RocksDBStore适配层
- BlueStore如何接入RocksDB
- 数据库打开与初始化流程
- 写路径分析
- 读路径与迭代器分析
- 列族与分片机制
- MergeOperator机制
- BlueFS与Env的关系
- 缓存、统计与compact
- reshard重分片流程
- 典型调用链总结
- 关键结论
文档目标
这份文档不是泛泛介绍 upstream RocksDB 的 LSM 树原理,而是专门分析:
- Ceph 里为什么要用 RocksDB
- BlueStore 如何通过
KeyValueDB抽象使用 RocksDB RocksDBStore如何把 Ceph 的 prefix/key 模型映射到底层 RocksDB- 事务、读写、迭代、列族、分片、merge、compact、重分片这些机制在 Ceph 中是怎么落地的
如果你正在阅读这些文件:
cephMain/src/kv/KeyValueDB.hcephMain/src/kv/KeyValueDB.cccephMain/src/kv/RocksDBStore.hcephMain/src/kv/RocksDBStore.cccephMain/src/os/bluestore/BlueStore.cc
那么这份文档的目标就是把它们串成一条完整主线。
整体定位
在 Ceph 的 BlueStore 架构中,对象数据 主要直接落到块设备上,而 元数据 则主要存放在 RocksDB 中。
可以把它粗略理解为:
- BlueStore 负责对象语义、空间管理、缓存、事务组织
- RocksDB 负责元数据的 KV 持久化
- BlueFS 负责为 RocksDB 提供底层文件存储环境
它们之间的分工如下:
| 层次 | 角色 | 主要职责 |
|---|---|---|
| OSD/BlueStore | 上层调用者 | 发起对象元数据读写、事务提交、迭代扫描 |
KeyValueDB |
抽象层 | 定义统一 KV 接口,屏蔽具体后端 |
RocksDBStore |
适配层 | 把 Ceph 接口映射为 RocksDB API |
| RocksDB | 元数据引擎 | 管理 memtable、WAL、SST、compaction、迭代器 |
| BlueFS / Env | 存储环境 | 提供 RocksDB 文件读写运行环境 |
因此,从 Ceph 代码视角看,真正值得重点理解的,不是 RocksDB 全部内部实现,而是:
KeyValueDB抽象语义RocksDBStore如何实现这个抽象- BlueStore 如何调用
KeyValueDB
核心代码位置
1. 抽象接口层
cephMain/src/kv/KeyValueDB.hcephMain/src/kv/KeyValueDB.cc
这层定义了通用 KV 数据库接口,包括:
- 事务对象
TransactionImpl - 读取接口
get() - 迭代器接口
IteratorImpl/WholeSpaceIteratorImpl - merge operator 抽象
- compact、缓存、统计等可选能力
2. RocksDB适配层
cephMain/src/kv/RocksDBStore.hcephMain/src/kv/RocksDBStore.cc
这层把上面的抽象映射为 RocksDB 的:
rocksdb::DBrocksdb::WriteBatchrocksdb::Iteratorrocksdb::ColumnFamilyHandlerocksdb::Optionsrocksdb::Env
3. BlueStore接入层
cephMain/src/os/bluestore/BlueStore.cc
BlueStore 通过 KeyValueDB::create() 创建数据库实例,通过 db->get_transaction()、db->submit_transaction()、db->get()、db->get_iterator() 等接口完成元数据操作。
抽象层KeyValueDB
KeyValueDB 的核心价值是:把 Ceph 上层的元数据访问模型统一成 prefix/key 语义。
1. prefix + key 模型
Ceph 不是简单把所有元数据塞进一个平面的 key 空间,而是按逻辑类别分前缀管理。例如:
- 某些前缀用于对象元数据
- 某些前缀用于统计信息
- 某些前缀用于 omap
- 某些前缀用于 allocator / freelist 相关元数据
所以 KeyValueDB 大部分接口都长这样:
1 | get(prefix, key, value) |
这意味着调用者思考的是“逻辑前缀 + 逻辑 key”,而不是 RocksDB 原始键编码细节。
2. 事务抽象
TransactionImpl 表示一批待提交的写操作,其职责是:
- 累积
set - 累积
rmkey - 累积
rm_range_keys - 累积
merge - 暴露操作数与字节规模
这层并不规定底层必须怎么实现,只规定“提交前是一批内存中的写集合,提交后一次性生效”。
在 RocksDBStore 中,这个抽象最终会映射成 rocksdb::WriteBatch。
3. 迭代器抽象
KeyValueDB 定义了两类迭代器:
IteratorImpl
面向 prefix 过滤后的逻辑视图WholeSpaceIteratorImpl
面向底层实现暴露的原始全键空间
这种两层设计非常重要,因为:
- 上层通常只关心某个 prefix 下的 key
- 实现层有时需要直接处理原始编码键,例如
"prefix\0key" - 当存在多个 column family / shard 时,实现层还可能需要做跨迭代器合并
4. MergeOperator抽象
KeyValueDB::MergeOperator 是对 RocksDB associative merge 的抽象包装。
它定义:
merge_nonexistent()merge()name()
其意义是:
- Ceph 上层只注册逻辑 merge 规则
- 具体怎么挂接到 RocksDB 默认 CF 或独立 CF,由
RocksDBStore负责
RocksDBStore适配层
RocksDBStore 是这套链路的核心。
它解决的问题是:
Ceph 的 prefix/key 抽象,如何安全且高效地映射到 RocksDB 的 DB / CF / Iterator / WriteBatch / Options / Env 体系。
1. 它管理的关键对象
RocksDBStore 内部最重要的成员包括:
rocksdb::DB *db
真正的 RocksDB 数据库实例rocksdb::Env *env
底层运行环境,可能是默认文件系统,也可能是BlueRocksEnvrocksdb::BlockBasedTableOptions bbt_opts
block cache、filter、index 等表选项default_cf
默认列族句柄cf_handles
逻辑 prefix 到实际列族分片句柄的路由表cf_ids_to_prefix
用于把 RocksDB column family id 反查回 prefixdbstats
RocksDB 统计对象
2. 它完成的核心映射
映射一:事务抽象 -> WriteBatch
KeyValueDB::TransactionImpl
映射成:
RocksDBStore::RocksDBTransactionImpl
其中核心成员就是:
rocksdb::WriteBatch bat
映射二:逻辑key -> RocksDB原始key
有两种模式:
没有独立列族时
原始 key ="prefix\0key"有独立列族时
prefix 直接决定列族,列族内只保存裸 key
映射三:prefix -> ColumnFamily
若 prefix 被配置为 column family,则:
- prefix 直接映射到某个 CF
- 如果该 prefix 进一步分片,则再根据 key 的 hash 路由到某个 shard CF
映射四:全空间迭代 -> prefix迭代
底层先提供 whole-space iterator,再按 prefix 做过滤包装;
如果实现层知道某个 prefix 已经独占某个 CF,则可以直接用更高效的单 CF iterator。
BlueStore如何接入RocksDB
BlueStore 自己并不直接 new rocksdb::DB,而是通过 KeyValueDB 工厂来创建。
1. 创建入口
KeyValueDB.cc 的工厂非常简单:
1 | KeyValueDB *KeyValueDB::create(CephContext *cct, const string& type, |
因此 BlueStore 只需要提供:
- 类型字符串
"rocksdb" - 路径
- KV 选项
- 可选
Env指针
就能拿到 RocksDBStore 实例。
2. BlueStore中的典型初始化动作
BlueStore 在初始化数据库时,通常会做三件事:
- 调用
KeyValueDB::create() - 调用
db->set_merge_operator()注册某些 prefix 的 merge 规则 - 调用
db->open()或db->create_and_open()
这意味着:
KeyValueDB负责统一入口RocksDBStore负责真实数据库打开- merge operator 必须在 open 前就注册完成
3. 运行期调用方式
BlueStore 后续主要通过以下接口使用 RocksDB:
get_transaction()submit_transaction()submit_transaction_sync()get()get_iterator()compact_*()estimate_prefix_size()
也就是说,BlueStore 使用的是“数据库抽象”,而非 RocksDB 原生 API。
数据库打开与初始化流程
RocksDBStore 的打开主流程核心在 do_open()。
1. init阶段
init() 做的事情很克制:
- 保存 options 字符串
- 试解析一遍配置,尽早发现错误
它不真正打开数据库。
这样 BlueStore 在 mount 前就能先验证 RocksDB 参数是否合法。
2. load_rocksdb_options阶段
load_rocksdb_options() 是选项构造中心,负责:
- 解析文本配置串
- 安装 Ceph 日志器
- 绑定
Env - 配置 block cache / row cache
- 配置 bloom filter / index type / table factory
- 安装默认 CF 的 merge router
- 配置 RocksDB statistics
可以理解为:这里把 Ceph 配置世界转换成 RocksDB Options 世界。
3. create_and_open路径
如果数据库尚不存在,则:
- 创建目录
- 打开 RocksDB
- 应用 column family / sharding 布局
- 持久化
sharding/def
4. open已有数据库路径
如果是打开已有数据库,则:
- 读取持久化的 sharding 定义
- 校验 RocksDB 实际列族与目标布局是否一致
- 打开已有列族
- 如处于重分片恢复阶段,则补建缺失列族
- 建立运行期
cf_handles路由表
这里最关键的一点是:
Ceph 在 reopen 时不是“盲开 RocksDB”,而是先验证逻辑分片布局与物理列族布局是否匹配。
写路径分析
1. 从BlueStore到事务对象
BlueStore 需要写元数据时,先拿一个事务:
1 | BlueStore |
此时所有写操作只是追加到 WriteBatch 里。
2. set路径
调用 set(prefix, key, value) 时,RocksDBTransactionImpl::set() 会先判断:
- 这个 prefix 是否有独立 CF
- 如果有,进一步判断是否要按 shard 分片
然后有两种写法:
情况一:prefix 落在 default CF
把 key 编码成:
prefix + '\0' + key
然后写入 default_cf
情况二:prefix 落在独立CF
直接把裸 key 写入目标 CF
3. 删除路径
删除有三种典型方式:
rmkey()rmkeys_by_prefix()rm_range_keys()
其中 rmkeys_by_prefix() 与 rm_range_keys() 里有一个非常重要的优化:
- 如果删除键数较少,则逐 key
Delete - 如果删除范围很大,则改用
DeleteRange
这样做的目的是在:
- 精确性
- 写放大
- tombstone 数量
之间做平衡。
4. merge路径
merge() 不会立即读出旧值再合并,而是把 merge 操作写入 RocksDB,由 merge operator 在后续读取或 compaction 时解释。
这适合:
- 计数器
- 统计项
- 增量聚合类元数据
5. 提交路径
所有事务最后都进入:
submit_transaction()或submit_transaction_sync()
它们最终都会调用:
submit_common()
submit_common() 负责:
- 设置
rocksdb::WriteOptions - 根据配置决定是否禁用 WAL
- 打印事务内容用于调试
- 调用
db->Write(woptions, &bat) - 采集 RocksDB perf context 数据
因此,Ceph 事务和 RocksDB WriteBatch 的关系可以概括为:
一个 Ceph KV 事务,最终就是一次 RocksDB db->Write()。
读路径与迭代器分析
1. get路径
RocksDBStore::get() 的主要工作依然是路由:
- 判断 prefix 是否映射独立 CF
- 若需要,则进一步选择 shard
- 调用 RocksDB
Get
同样分两种情况:
- default CF:读
"prefix\0key" - 独立 CF:读裸 key
2. split_key的意义
默认 CF 中,原始键是组合键:
prefix\0key
因此 split_key() 是一个很基础但非常重要的辅助函数。
它的作用是把 RocksDB 原始 key 拆回:
- prefix
- 逻辑 key
很多迭代器逻辑都依赖它。
3. WholeSpaceIterator
WholeSpaceIteratorImpl 表示底层数据库实现直接暴露的原始迭代器。
在 RocksDB 场景下,它通常对应:
- 某个 column family 上的
rocksdb::Iterator
它能返回:
key()raw_key()value()value_as_sv()
这里的 raw_key() 特别重要,因为它可能是未过滤 prefix 的真实编码键。
4. PrefixIterator
KeyValueDB 默认提供一个 PrefixIteratorImpl 包装器,它的作用是:
- 底层先拿 whole-space iterator
- 外层再按 prefix 过滤
这保证即使某个后端没有实现更高级的原生 prefix iterator,也能对上层暴露统一行为。
5. RocksDBStore中的优化
RocksDBStore 不只是使用通用 PrefixIteratorImpl,它还会根据:
- prefix 是否独占某个列族
- 迭代边界是否落在单个 shard
来决定是否直接创建更高效的单列族 iterator。
这就是为什么 RocksDBStore 里会有:
new_shard_iterator()check_cf_handle_bounds()- 跨 shard 的 merge iterator
列族与分片机制
这是 Ceph 对 RocksDB 使用方式里最关键、也最“非通用 RocksDB 教材式”的部分。
1. 为什么需要列族
如果所有 prefix 都放在 default CF 中,会遇到几个问题:
- 不同类型元数据混在一起
- compaction 相互影响
- 缓存难以差异化
- 热点前缀容易形成瓶颈
因此 Ceph 支持把某些 prefix 提升为独立 column family。
2. 为什么还需要分片
即使一个 prefix 独占一个 CF,仍可能有问题:
- 某个前缀的数据量太大
- 某个前缀写入太热
- 单 CF compaction 压力太集中
因此 Ceph 允许一个逻辑列族继续拆成多个 shard:
1 | O -> O-0, O-1, O-2, O-3 ... |
3. 分片定义字符串
Ceph 使用文本字符串描述 sharding 布局,例如:
1 | O(4,0-10)=write_buffer_size=... |
它描述的是:
- 逻辑列族名
- shard 数量
- 用 key 的哪一段参与 hash
- 该列族附加的 RocksDB 选项
parse_sharding_def() 就是这个语法的解析器。
4. 路由规则
路由过程是:
- 通过 prefix 找到逻辑列族
- 如果只有一个 handle,直接落该 CF
- 如果有多个 shard,则取 key 的某个子串做 hash
hash % shard_count选出最终 shard
5. 运行期持久化
Ceph 会把当前 sharding 定义写到:
sharding/def
这样下次重启时可以:
- 校验 RocksDB 中真实存在的列族
- 恢复重分片状态
- 拒绝错误布局的数据库
BlueStore各Prefix的Key/Value格式总表
下面这张表总结的是 BlueStore 传给 KeyValueDB 的逻辑格式。
真正落到 RocksDB 时,RocksDBStore 还会再做一层物理映射:
- 若该 prefix 落在 default CF,则物理 key =
prefix + '\0' + logical_key - 若该 prefix 有独立 CF,则物理 key =
logical_key
因此表中重点关注的是 BlueStore 自己定义的 logical_key 与 value 编码规则。
| Prefix | 主要用途 | logical key 格式 | value 格式 | 说明 |
|---|---|---|---|---|
S |
super 元数据 | 通常是可读字段名,如 nid_max、blobid_max、ondisk_format、min_alloc_size |
对应字段的 encode() 后 bufferlist |
保存 BlueStore 全局配置、版本、计数器、高水位等 |
T |
统计信息 | 可读字段名,或 pool_id 的二进制编码 key |
统计结构编码后的 bufferlist,常通过 merge 更新 | BLUESTORE_GLOBAL_STATFS_KEY 也存放在这里 |
C |
collection 元数据 | stringify(cid) |
cnode_t 的编码 |
保存 collection 的 bits 等元数据 |
O |
onode / 对象元数据 | ghobject_t 的排序友好二进制编码:shard + pool + hash + namespace + key/name + snap + generation + 'o' |
bluestore_onode_t + spanning blobs + inline extents 编码后的 bufferlist |
BlueStore 最核心的一类记录 |
M |
普通 omap | u64 nid + '.' + user_key;边界键分别用 '-' 和 '~' |
header/tail 特殊值,普通项为用户 omap value | 默认对象 omap |
P |
pgmeta omap | u64 nid + '.' + user_key;不带 pool/hash 前缀 |
与 M 相同 |
用于 meta collection 的 omap |
m |
per-pool omap | u64 pool + u64 nid + '.' + user_key |
与 M 相同 |
把 pool 维度编码进 key 前缀 |
p |
per-pg omap | u64 pool + u32 hash + u64 nid + '.' + user_key |
与 M 相同 |
比 per-pool 再多一层 hash 维度 |
L |
deferred 事务 | u64 seq |
deferred_transaction_t 的编码 |
启动回放 deferred write 就扫这类键 |
X |
shared blob 元数据 | u64 sbid |
bluestore_shared_blob_t 的编码 |
保存 shared blob 引用关系等元数据 |
B |
freelist 空间管理 | u64 offset |
u64 length |
经典 freelist 形式,表示某段空闲区间 |
b |
bitmap freelist 空间管理 | 由 BitmapFreelistManager 定义的位图键 |
位图相关结构编码 | 属于 bitmap allocator 的内部格式 |
1. S:super元数据
这类记录最直观,key 往往就是字段名,value 是对应字段的编码结果。例如:
nid_maxblobid_maxfreelist_typeondisk_formatmin_compat_ondisk_formatmin_alloc_sizeper_pool_omap
它们的共同特点是:
- key 可读
- value 一般是定长标量或简单结构的
encode()结果 - 用于 BlueStore 启动时恢复全局状态
2. T:统计信息
T 类记录主要用于:
- 全局 statfs
- per-pool 统计
这里有两类常见 key:
- 可读字符串 key,例如:
bluestore_statfs
- 二进制 pool 统计 key:
u64(pool_id)
这类 value 常不是“整块重写”,而是通过 merge() 增量更新。
3. C:collection元数据
C 类记录较简单:
- key =
stringify(cid) - value =
cnode_t
典型作用是保存:
- collection 的 bits
- collection 的基础拓扑元信息
4. O:对象onode元数据
O 是最重要的一类。
key 格式
对象 key 是按排序需求定制的二进制编码,核心顺序是:
shardpoolhashnamespacekey/object namesnapgeneration- 后缀字符
'o'
它的目标不是人可读,而是:
- 能按对象逻辑顺序扫描
- 能支持范围列举
- 能支持
lower_bound/upper_bound
value 格式
O 类 value 由三部分拼接而成:
bluestore_onode_t- spanning blob 信息
- inline extent map 数据
如果 extent map 已经分片,则分片内容不一定都 inline 放在这个 value 里。
5. M/P/m/p:omap家族
omap 的关键点是:底层 key 不是直接等于用户 omap key。
它会在用户 key 前面加上对象/池/PG 相关前缀,以便:
- 把同一个对象的 omap 放在连续范围里
- 构造 header/key/tail 三种边界
三类边界字符是:
'-':header'.':实际用户 key'~':tail
所以同一个对象的 omap 区间通常长这样:
1 | ... + nid + '-' |
不同变种区别在于 nid 前面是否再编码:
M:只有nidP:pgmeta 场景,也只有nidm:pool + nidp:pool + hash + nid
6. L:deferred事务
L 类记录表示尚未完全回放/清理的 deferred 写事务。
- key =
u64 seq - value =
deferred_transaction_t
启动恢复时,BlueStore 会扫描 PREFIX_DEFERRED,逐条回放。
7. X:shared blob
X 类记录保存 shared blob 元数据。
- key =
u64 sbid - value =
bluestore_shared_blob_t
value 里最关键的是:
- shared blob 标识
- ref_map
- 引用计数相关状态
8. B:传统freelist
B 类记录表示空闲空间区间:
- key =
u64 offset - value =
u64 length
它描述的语义很直接:
从
offset开始,有一段长度为length的空闲物理空间。
9. b:bitmap freelist
b 类记录属于 bitmap allocator 的内部格式。
它和 B 的区别是:
B偏向“区间列表”b偏向“位图块”
这里的 key/value 具体布局不在 BlueStore.cc 里定义,而由 BitmapFreelistManager 负责。
如果你继续往下看 allocator 路径,重点就该转到 bitmap freelist 的实现文件。
MergeOperator机制
1. 为什么要做两层适配
RocksDB 每个 column family 只能配置一个 merge operator。
但 Ceph 的逻辑模型是:
- 不同 prefix 可能需要不同 merge 语义
如果所有这些 prefix 都落在 default CF,就会出现冲突:
- default CF 里混着多个 prefix
- 每个 prefix 却想用不同 merge 规则
因此 RocksDBStore 设计了两种适配器:
2. MergeOperatorRouter
用于 default CF。
它的逻辑是:
- 从原始 key 中识别 prefix
- 在已注册的 merge operator 列表中找到对应 prefix
- 把 merge 委托给对应 Ceph
MergeOperator
3. MergeOperatorLinker
用于非 default CF。
因为独立 CF 对应单一逻辑 prefix,所以不需要“路由”,只需要直接绑定。
4. 为什么要构造稳定名称
RocksDB 在打开数据库时会校验 merge operator 名称是否一致。
因此 RocksDBStore 会把:
- prefix
- operator name
组合成稳定的整体名字,以保证:
- 注册顺序变化不会影响最终名称
- open 时能正确做一致性检查
BlueFS与Env的关系
1. Env是什么
rocksdb::Env 是 RocksDB 对底层文件环境的抽象。
RocksDB 的很多操作并不直接调用 POSIX 文件 API,而是通过 Env:
- 创建文件
- 读写 WAL
- 读写 SST
- 创建目录
- 后台线程
2. Ceph为什么要定制Env
在 BlueStore 场景里,RocksDB 文件并不一定落在普通文件系统目录中,而可能落在:
- BlueFS 管理的设备空间
因此 BlueStore 会在合适条件下构造:
BlueRocksEnv
再把它作为 void* p 传给 KeyValueDB::create()
然后 RocksDBStore 在构造或 open 时把它转回:
rocksdb::Env *env
3. 结果是什么
RocksDB 仍以为自己在使用标准 Env,
但实际底层文件已经由 BlueFS 承载。
这使得 Ceph 实现了:
- RocksDB 逻辑不大改
- BlueFS 接管底层存储
- WAL/SST 与 BlueStore 设备布局协同工作
缓存、统计与compact
1. 缓存
RocksDBStore::load_rocksdb_options() 里会统一构造缓存体系:
- block cache
- row cache
- bloom filter
- index type
Ceph 还支持:
- 全局 RocksDB cache 大小配置
- 某些列族单独 block cache 配置
- 优先级缓存接口
PriorityCache
这说明 Ceph 并不只是“用一下 RocksDB”,而是在缓存资源分配上做了比较深的整合。
2. 统计
RocksDBStore 支持:
- RocksDB statistics
- RocksDB perf context
- Ceph perf counters
因此既能看:
- RocksDB 内部写 WAL / memtable / delay 的耗时
也能看:
- Ceph 视角的 get latency / submit latency / compact 指标
3. compact
Ceph 暴露了多种 compact 入口:
- 全量 compact
- prefix compact
- range compact
- async compact
RocksDBStore 还维护了一个后台 compact 队列,用来:
- 合并相邻 compact 范围
- 避免前台线程直接承担长时间 compact
reshard重分片流程
reshard 是 RocksDBStore 里最复杂的一类维护逻辑之一。
它的本质是:
把已有数据库中的 key,从旧的列族布局搬迁到新的列族布局。
1. 为什么需要reshard
因为随着系统运行,最初的列族布局可能不再适合:
- 数据量变大
- 某类前缀成为热点
- compaction 压力集中
- 需要新的缓存策略
2. reshard的核心步骤
- 解析目标 sharding 定义
- 写入重分片锁
- 列出现有列族
- 打开所有旧列族
- 计算缺失的新列族并创建
- 构造新的
cf_handles - 遍历旧列族全部 key
- 根据新规则重算目标 CF
- 批量执行
Delete + Put - 删除旧布局中不再需要的空列族
- 写回新的
sharding/def
3. 为什么实现里有很多“失败注入”开关
resharding_ctrl 里有:
unittest_fail_after_first_batchunittest_fail_after_processing_columnunittest_fail_after_successful_processing
这是因为重分片是一个跨多步的维护过程,最怕中途失败导致状态不一致。
所以实现必须支持:
- 故障注入
- 启动后恢复
- 继续完成中断的重分片
典型调用链总结
1. 初始化调用链
1 | BlueStore |
2. 写事务调用链
1 | BlueStore |
3. 单key读取调用链
1 | BlueStore |
4. 迭代器调用链
1 | BlueStore |
5. merge调用链
1 | BlueStore |
关键结论
1. KeyValueDB是Ceph真正依赖的接口
Ceph 上层并不直接依赖 RocksDB 原生 API,而是依赖 KeyValueDB 抽象。
2. RocksDBStore是关键桥梁
RocksDBStore 负责把:
- Ceph 的 prefix/key 模型
- 事务模型
- 迭代器模型
- merge 模型
映射到 RocksDB 的对象体系中。
3. Ceph对RocksDB的使用并不“浅”
Ceph 并不是简单把 RocksDB 当成一个黑盒 KV 库,而是深度利用了:
- column family
- merge operator
- Env
- WriteBatch
- iterator bounds
- perf context
- block cache
- DeleteRange
- 重分片
4. BlueFS是RocksDB接入BlueStore设备体系的关键
没有 BlueFS / BlueRocksEnv,RocksDB 只能把文件落到普通文件系统;
有了它,Ceph 才能把 RocksDB WAL/SST 纳入 BlueStore 的设备管理体系。
5. 读这部分代码的最佳顺序
建议按下面顺序阅读:
KeyValueDB.hKeyValueDB.ccRocksDBStore.hRocksDBStore.ccBlueStore.cc中数据库初始化、读写事务、iterator 调用部分
如果按这个顺序看,理解成本会明显低很多。
正在加载留言…