土法炼钢兴趣小组的算法知识备份

【RocksDB 内核机制】RocksDB 架构演进:相对 LevelDB 的 diff 地图

文章导航

分类入口
databasestorage
标签入口
#rocksdb#leveldb#column-family#rate-limiter#direct-io#statistics#write-thread#flush-job#compaction-job

目录

LevelDB 基线DBImpl::WriteGet 与单线程 BackgroundCompaction 钉在 google/leveldb 1.23 源码上。Facebook 2012 年 fork 后,RocksDB 在 多核利用、嵌入场景、可观测性 三条线上持续演进;到 9.x 主线(本系列锚定 facebook/rocksdb 9.10.0 及相邻 9.x release,见 HISTORY.md),DBImpl 已拆成多个翻译单元,线程模型与 LevelDB 几乎无可比性。

本文是系列第 3 篇,不做性能 benchmark(未在本环境跑对比测试),只交付 架构 diff 地图:每一项改动对应什么生产问题、落在哪些源文件、与 LevelDB 行为的边界差异。读完应能打开 RocksDB 树,从 include/rocksdb/db.h 的 API 跳到 db/db_impl/db_impl_write.ccWriteImpl,而不在 ColumnFamilySetWriteThread 之间迷路。

本文是「RocksDB 内核机制」系列第 3 篇(共 18 篇)。→ 系列目录

篇目 核心内容
第 2 篇 · LevelDB 基线 单线程 leveled 骨架
第 3 篇 · RocksDB 架构演进 LevelDB vs RocksDB diff 地图
第 4 篇 · WAL 与 WriteBatch WriteImpl 逐步展开
第 5 篇 · MemTable 与 Flush FlushJob 与 immutable 队列

版本锚定:RocksDB 9.x(源码 post/db/rocksdb/reproduce/rocksdb/);LevelDB 1.23(上篇基线)。

环境说明:WSL2;本篇机制来自 RocksDB Wiki RocksDB vs LevelDBHISTORY.md 与源码目录结构,不引用未执行的吞吐数据。


一、fork 动机:LevelDB 的三条硬边界

LevelDB 在 2011 年满足 Chrome IndexedDB 等场景的 嵌入式、顺序写优先 需求。Facebook 迁移到 MyRocks 等 workload 时,三条边界最先撞墙:

  1. CPU:单 BackgroundCompaction 无法喂饱多核与高速 SSD。
  2. 命名空间:同一 DB 内无法隔离多组 comparator / compaction 选项(Column Family 需求)。
  3. 可运维性:缺少结构化 Statistics、I/O 限流、Direct I/O,大状态 DB 难以诊断与控资源。

RocksDB 选择 保留 LSM 磁盘语义(WAL、SST 不可变、MANIFEST、InternalKey + sequence),在 DBImpl 外围加线程池、CF 抽象与监控钩子。Wiki 页 RocksDB vs LevelDB 是官方 diff 清单;下表在本系列语境下补充 源码路径后续篇目


二、LevelDB vs RocksDB 总览 diff 表

维度 LevelDB 1.23 RocksDB 9.x 主要源码 / 文档
后台 flush 与 compaction 共用 1 个 Env::Schedule 独立 FlushJob,可并行多个 immutable memtable db/flush_job.*db/flush_scheduler.*
后台 compaction 单线程 DoCompactionWork CompactionJob + 线程池,多 job 并发 db/compaction/compaction_job.*
逻辑分区 Column Family,共享 WAL、独立 LSM db/column_family.*
并发写 MemTable 队首线程 group commit WriteThread 流水线,可 allow_concurrent_memtable_write db/write_thread.*db_impl_write.cc
I/O 限流 RateLimiter(flush/compaction/read/write 分类) util/rate_limiter.*
绕过 OS 页缓存 依赖 mmap / read Direct I/O 可选(use_direct_reads/writes Wiki Direct IOenv/*
监控 info_log 文本 StatisticsPerfContext、持久化 stats CF monitoring/*include/rocksdb/statistics.h
Compaction 策略 Leveled Leveled + Universal + FIFO + TTL 等 db/compaction/*
事务 / 备份 OptimisticTransactionDB、Checkpoint、BackupEngine utilities/*
过滤器 Bloom Bloom + Ribbon、Partitioned Filter table/block_based/*
API 面 DB DBColumnFamilyHandleWriteBatchWithIndex、… include/rocksdb/*.h

以下各节展开 对上表右侧 的机制,不重复 LevelDB 已讲过的 WAL/MemTable 最小语义。


三、源码布局:从单文件 DBImpl 到子系统目录

LevelDB 的核心实现在 db/db_impl.cc(约 1500 行)。RocksDB 9.x 将 DBImpl 拆散以便并行维护:

db/db_impl/
  db_impl.cc          # 构造、Open、Close、属性
  db_impl_write.cc    # WriteImpl、WAL、WriteThread 交互
  db_impl_compaction_flush.cc
  db_impl_read.cc     # Get、MultiGet
  db_impl_open.cc     # 恢复、MANIFEST replay
db/column_family.*
db/flush_job.*
db/compaction/compaction_job.*
db/write_thread.*
db/version_set.*

阅读顺序建议:db.h API → db_impl_write.cc::WriteWriteImplMemTableListFlushJob(第 4–5 篇);读路径走 db_impl_read.cc(第 7 篇)。

RocksDB 仍保留 LevelDB 版权头与 ROCKSDB_NAMESPACE 命名空间;许多类名(VersionSetWriteBatchTableCache)直接延续,降低从 LevelDB 跳转的认知成本。


四、学术谱系:从单线程 LevelDB 到 bLSM

LevelDB 与 RocksDB 的 diff 不是「重写 LSM」,而是 在同一磁盘语义上并行化后台。读 diff 表时应能回答:多线程 compaction 在文献里对应什么、RocksDB 相对论文改了什么

阶段 系统 / 文献 相对前一代
1996 O’Neil et al., LSM-Tree 定义顺序写 + 后台归并(第 1 篇 §2.1
2011 LevelDB(Dean & Ghemawat) 单进程 leveled + 单后台 worker第 2 篇
2012 bLSM(Sears & Ramanan, SIGMOD) Many-core compaction、控制 L0 run 以减 stall
2012+ RocksDB fork 线程池 flush/compaction、CF、RateLimiter、Statistics(本篇 diff 表)

工程结论:bLSM 论文证明 并行 compaction 可缩短 stall 窗口;RocksDB 的 CompactionJob + Env 线程池 + NeedSpeedupCompaction()工业实现路径,与 bLSM 源码 不一一对应(以 RocksDB 9.x 源码为准)。

4.1 工程间隙

论文 / LevelDB 假设 MyRocks / Flink / TiKV
单机、单 CF 多 CF、多 subtask 目录
单 BG 线程瓶颈 磁盘带宽与 checkpoint 读 成新瓶颈
无 RateLimiter 云盘 IOPS 配额 + 引擎内 limiter 叠层

4.2 开放问题

  1. Disaggregated compaction:compaction 是否应远程执行以换本地写带宽?产品与 NSDI/OSDI 存储分离论文并存(db-frontier/14)。
  2. CF 扩展 vs 多 DB:Column Family 共享 WAL 的 stall 传染 是否在 skew 下劣于多进程?社区实践分裂,无顶会统一结论
  3. Ribbon / 新 filter 默认化:读路径 CPU 与 compaction 构建成本如何影响 嵌入 JVM 场景?以 release note 为准(第 9 篇)。

五、多线程 Flush 与 Compaction

LevelDB 回顾

单个 BackgroundCompaction:若 imm_ != nullptrCompactMemTable(),否则 PickCompaction()。flush 与 level compaction 互斥

RocksDB 演进

flowchart LR
  subgraph leveldb["LevelDB"]
    BG1["Single BG thread"]
    BG1 --> FL1["CompactMemTable"]
    BG1 --> CP1["DoCompactionWork"]
  end
  subgraph rocksdb["RocksDB 9.x"]
    POOL["ThreadPool Env"]
    POOL --> FJ["FlushJob x N"]
    POOL --> CJ["CompactionJob x M"]
  end

生产含义:Flink 状态后端在 checkpoint 对齐后大量 flush,若 max_background_flushes 过小,immutable memtable 队列变长——与 stream/12 讨论的 checkpoint I/O 争抢直接相关(第 16 篇量化边界,本篇不伪造耗时)。


六、Column Family:共享 WAL,独立 LSM

Column Family(CF)是 RocksDB 相对 LevelDB 最大的结构扩展。每个 CF 拥有:

默认 CF 名 defaultdb_impl.cckDefaultColumnFamilyName)。ColumnFamilyDescriptorOpen 时注册;运行时 CreateColumnFamily 追加新 LSM 树。

TiKV、Flink 用 CF 隔离 不同 state 变量或模块(例如 Flink 为 ValueStateMapState 分配不同 CF)。第 13 篇会讲 ColumnFamilyData 与 WAL 回收的交互;本篇只记 边界:LevelDB 的「整库一棵 LSM」在 RocksDB 变为「整库一棵 WAL + 多棵 LSM」。


七、WriteThread 与并发 MemTable 写

LevelDB 用 writers_ deque + 条件变量:仅队首线程写 WAL 与 mem_。RocksDB 引入 WriteThreaddb/write_thread.h):

对外 API 仍是一个 DB::Write

Status DBImpl::Write(const WriteOptions& write_options, WriteBatch* my_batch) {
  // ...
  s = WriteImpl(write_options, my_batch, /*callback=*/nullptr,
                /*user_write_cb=*/nullptr, /*log_used=*/nullptr);
  return s;
}

(摘自 RocksDB 9.x db/db_impl/db_impl_write.cc。)

WriteImpl 内部协调:WriteThread 入队 → 分配 sequence → WAL(可能 ConcurrentWriteToWAL)→ 各 CF 的 MemTable → 更新 VersionSet 的 last sequence。第 4 篇按调用顺序逐步展开;与 LevelDB 的 diff 核心是 WriteThread 状态机 替代简单 writers_ 队列。


八、RateLimiter:区分前台与后台 I/O

RocksDB 的 RateLimiterNewGenericRateLimiter)按 I/O 优先级 分配字节令牌:

Wiki Rate Limiter 说明:rate_bytes_per_sec 为上限,可 auto_tuned 根据 backlog 调节。LevelDB 无等价物;compaction 与前台写争抢磁盘时只能依赖 OS 调度。

绑定路径:compaction/flush 的 WritableFileWriter 在写 SST 前向 limiter Request()DBOptions::rate_limiter 指针注入全库。第 12 篇用 db_bench 对比限流开/关(实验在 reproduce/,未跑则不写数字结论)。


九、Direct I/O 与压缩

Direct I/O

默认路径经 page cache(mmap 或 read)。大 DB 下 compaction 读 SST 与用户读共享 cache,易出现 双缓冲(cache + compaction 缓冲)。use_direct_reads / use_direct_writes(及 CF 级 TableOptions)让 SST I/O 绕过 OS cache,配合 block_cache 由 RocksDB 自行管理热点块。

Wiki Direct IO 强调:需块对齐、与 block_size 配合;不是所有文件系统组合都受益。LevelDB 无 first-class 支持。

压缩

RocksDB 支持 Snappy、Zlib、LZ4、ZSTD 等 per-level 压缩策略(compression_per_level)。LevelDB 主要 Snappy。压缩影响 写放大(CPU 换空间)与 读路径 CPU;不是架构 diff 的核心,但出现在 TableBuilderFlushJob 配置中。


十、Statistics 与可观测性

LevelDB 依赖 options.info_log 人工 grep。RocksDB 提供:

机制 用途
Statistics 计数器:rocksdb.block.cache.hit/missrocksdb.compaction.key.drop
PerfContext 单次 Get/Iterator 的微结构耗时
IOStatsContext 线程级 I/O 统计
Persistent stats CF 历史 stats 落盘(___rocksdb_stats_history___ CF)

DB::GetProperty("rocksdb.stats") 文本摘要与 LOG 中的 compaction 事件,是 第 17 篇 排查 L0 堆积的入口。Statistics 不改变 LSM 语义,但使 stall 与放大 从「感觉慢」变为可核对指标。


十一、读路径与 SuperVersion(预告)

LevelDB 在 Get 里 Ref mem_imm_Version::current()。RocksDB 引入 SuperVersion:原子快照 所有 CF 的 MemTable 列表 + 当前 Version 指针,避免读路径长时间持 DB mutex

SuperVersion 切换在 flush/compaction 提交 LogAndApply 后;旧 SuperVersion 引用计数归零后回收 immutable memtable。第 7 篇 Get 篇会沿 db_impl_read.cc 展开;本篇只标记 diff:多 CF + SuperVersion 使读路径比 LevelDB 多一层间接,但单次点查逻辑仍为 MemTable 链 → L0…Ln。


十二、Compaction 策略扩展

LevelDB 仅 leveled compactionleveldb::Options::compaction_style implicit)。RocksDB 增加:

CompactionPicker 接口化;leveled 不变式(L0 重叠、L1+ 不重叠)仍是默认生产配置,与 storage/31 讨论一致。策略 diff 的源码主战场是 db/compaction/*(第 10–11 篇)。


十三、API 与工具面增量(选型视角)

RocksDB 在 LevelDB DB 之上增加的常用能力(不全列,见 include/rocksdb/db.h):

LevelDB 的 WriteBatchSnapshotIterator 语义保留;迁移 RocksDB 的代码通常只需改 namespace 与 Options,直到需要 CF 或多线程调优。


十四、diff 地图与系列后续篇目

flowchart TB
  L["LevelDB 1.23<br/>single BG + no CF"]
  R["RocksDB 9.x"]
  L -->|"WriteThread CF"| R
  L -->|"FlushJob CompactionJob"| R
  L -->|"RateLimiter Statistics"| R
  R --> P4["Ch04 WAL WriteImpl"]
  R --> P5["Ch05 MemTable Flush"]
  R --> P10["Ch10-12 Compaction Stall"]
  R --> P13["Ch13 Column Family"]
  R --> P16["Ch16 Flink TiKV embed"]
diff 主题 本系列展开篇目
WAL + WriteBatch + group commit 第 4 篇
MemTable + FlushJob 第 5 篇
SST + MANIFEST + VersionSet 第 6 篇
Get + Snapshot + SuperVersion 第 7 篇
Iterator + Merge 第 8 篇
Block cache + Bloom/Ribbon 第 9 篇
Leveled / Universal + stall 第 10–11 篇
RateLimiter + 并发 compaction 第 12 篇
Column Family 第 13 篇
Flink / TiKV 嵌入 第 16 篇

十五、小结

RocksDB 不是重写 LSM,而是在 LevelDB 骨架上 并行化后台、拆分 CF、暴露监控与限流。理解 diff 地图后,阅读 9.x 源码应带着三个问题:这条路径 LevelDB 有没有?若有,是单线程还是线程池?WAL 是整库共享还是 per-CF 逻辑隔离?

下一篇进入写路径细节:WAL record 格式、WriteBatch 原子性、WriteImpl 里 WAL 与 MemTable 的提交顺序——直接对接本系列实验台账中的 WAL 目录观测。


参考资料

  1. RocksDB Wiki, RocksDB vs LevelDB(官方 diff 清单)。
  2. Sears & Ramanan, bLSM, SIGMOD 2012(多核 compaction;A 级)。
  3. O’Neil et al., LSM-Tree, Acta Informatica 1996(LSM 奠基;A 级,详见 第 1 篇)。
  4. RocksDB Wiki, Architecture / Rate Limiter / Direct IO
  5. RocksDB 源码 facebook/rocksdb 9.x:db/db_impl/db_impl_write.ccWriteWriteImpl)、db/write_thread.*db/column_family.*db/flush_job.*db/compaction/compaction_job.*
  6. RocksDB HISTORY.md(9.x release 行为变更边界)。
  7. Google LevelDB 1.23 db/db_impl.cc(上篇基线)。
  8. 本系列第 2 篇第 4 篇
  9. storage/31 LSM 调优(compaction 策略与放大)。

返回 系列目录 | 上一篇:LevelDB 基线 | 下一篇:WAL 与 WriteBatch

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。

2026-07-07 · database / storage

【RocksDB 内核机制】并发 Compaction 与 Rate Limiter

从 CompactionJob 的 Prepare/Run/Install 与 subcompaction 并行,到 Env 高低优先级线程池、MaybeScheduleFlushOrCompaction 调度,以及 GenericRateLimiter 的 kWritesOnly 与 DelayWrite 的争抢关系;exp12 三路中位数对比方法论。

2026-07-07 · database / storage

【RocksDB 内核机制】Column Family:共享 WAL 与独立 LSM

从 RocksDB 3.0 引入的 Column Family 出发,拆解共享 WAL、独立 MemTable/SST/Version 链、`ColumnFamilyHandle` 生命周期与 DBOptions/ColumnFamilyOptions 分层;并对照 Flink 多 state 变量到 CF 的映射边界。


By .