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

【RocksDB 内核机制】LSM 生态全景:存储引擎三角与 RocksDB 生态位

文章导航

分类入口
databasestorage
标签入口
#rocksdb#lsm-tree#leveldb#storage-engine#tikv#flink#kafka-streams#clickhouse#storage-triangle

目录

读完 PostgreSQL 内核系列 的行存 B-Tree + MVCC,以及 列存引擎系列 的读优化 scan 之后,写优化 LSM 往往仍停在两个入口:lsm-tree DIY 五篇 的 toy engine,或 storage/32 RocksDB 实践 的参数模板。与此同时,TiKV、Flink EmbeddedRocksDBStateBackend、Kafka Streams 本地状态目录里实际跑的是 RocksDB——stream-processing 第 12 篇 从 Flink 视角讲了 KeyGroup 与增量 checkpoint,但没有按 DBImpl::WriteCompactionJob、write stall 的源码路径系统拆解。

本文是「RocksDB 内核机制」系列第 1 篇。目标不是重复 LSM 三种放大的推导(见 lsm-tree 第 1 篇storage/31),而是回答三个定位问题:

  1. B-Tree 行存 / 列存 scan / LSM 写优化 构成的存储三角里,RocksDB 占哪一角、适合什么负载?
  2. TiKV、Flink、Kafka Streams、ClickHouse Embedded RocksDB 各自把 RocksDB 嵌在什么层次?
  3. 本系列 18 篇与相邻系列的分工边界在哪里?

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

篇目 核心内容
第 1 篇 · LSM 生态全景 存储三角、生产嵌入生态位、系列地图
第 2 篇 · LevelDB 基线 单线程 leveled、DBImpl::Write/Get 骨架
第 3 篇 · RocksDB 架构演进 相对 LevelDB 的 diff 地图
第 4 篇 · WAL 与 WriteBatch 持久化与原子批写

版本锚定:RocksDB 9.x(本系列源码路径与 Wiki);LevelDB 1.23(对照基线);Flink 1.20+(第 16 篇引用边界)。

环境说明:本机 WSL2(内核 6.6.x),RocksDB 源码位于 post/db/rocksdb/reproduce/rocksdb/。本篇以架构与生态定位为主,不粘贴未执行的 db_bench 数字;机制结论来自 RocksDB Wiki、LevelDB/RocksDB 源码与官方嵌入文档。


一、存储引擎三角:三种优化方向

关系型 OLTP、分析型 scan、嵌入式 KV 日志层面对的不是同一组瓶颈。把它们粗分为三角,便于选型和阅读路径对齐——三角不是互斥,而是 默认优化方向 不同。

flowchart TB
  subgraph triangle["Storage Engine Triangle"]
    PG["Row OLTP<br/>B-Tree + MVCC"]
    COL["Columnar Scan<br/>Vectorized Read"]
    LSM["Write-Optimized LSM<br/>RocksDB / LevelDB"]
  end
  PG --- COL
  COL --- LSM
  LSM --- PG
代表实现 前台写路径 前台读路径 典型场景
行存 B-Tree PostgreSQL Heap + B-Tree、InnoDB 随机页写 + WAL 索引定位 + 缓冲池命中 OLTP、事务、等值/范围查询均衡
列存 scan ClickHouse MergeTree、DuckDB Columnar 批量 part 追加 + 后台 merge 列块向量化 scan + 谓词下推 OLAP、报表、聚合为主
写优化 LSM LevelDB、RocksDB WAL 顺序追加 + MemTable 内存写 MemTable → L0…Ln 层级查找 写密集 KV、流状态、分布式 KV 本地引擎

行存 B-Tree 角

PostgreSQL 内核 已经按源码路径讲过:Backend 进程、Buffer Manager 页缓存、B-Tree 索引、WAL 与 MVCC 快照。B-Tree 的前台写路径本质是 定位叶子页 + 原地更新(或分裂),至少一次随机读加一次随机写。读路径在索引命中时接近 \(O(\log n)\) 次页访问,配合缓冲池可以压到内存。

这一角的优势是 事务语义、范围扫描、读延迟稳定。代价是写密集场景下随机 I/O 成为瓶颈——这正是 LSM-Tree 要解决的问题(见下一节谱系)。

列存 scan 角

列存引擎系列 从 MergeTree part 布局讲到向量化执行:数据按列存储、按批读取,聚合与 filter 在 CPU cache 友好的列块上完成。写路径通常是 追加新 part + 后台 merge,与 LSM 的「只追加、后台归并」有结构相似性,但 列存优化的是分析 scan,不是通用点查 KV;也不提供 RocksDB 那种嵌入式 Put/Get API。MonetDB/X100(Boncz et al., SIGMOD 2005)与后续向量化 OLAP 论文定义的是 scan 向量化 范式,与 LSM 的 点查 + 范围 Iterator 优化目标不同(columnar-engine/04)。

LSM 写优化角

lsm-tree 第 1 篇 推导了写放大 \(WA\)、读放大 \(RA\)、空间放大 \(SA\) 的定义与 leveled compaction 的数量级。核心机制不变:

\[ \text{前台写} \approx \text{WAL append} + \text{MemTable insert} \]

Compaction 在后台把随机写「摊平」成顺序 I/O,但同一份用户数据可能被多次写入磁盘。RocksDB 是这一角在 生产环境 的默认实现之一:Facebook 2012 年 fork LevelDB,面向多核、SSD、大状态嵌入场景持续演进(RocksDB Wiki, Architecture)。


二、学术谱系:从 LSM 论文到 RocksDB

工程三角背后有一条可核对的 论文—系统 链。读本系列时,至少应能回答:今天 leveled compaction 从哪来、与 tiering 争论什么、RocksDB 相对论文假设改了什么。

2.1 奠基:LSM-Tree 与 B-Tree 分工

工作 会议/期刊 本文引用点
O’Neil et al., The Log-Structured Merge-Tree (LSM-Tree) Acta Informatica, 1996 定义 先日志、后排序、后台归并;写路径顺序化;\(WA\)/\(RA\) 权衡的原始框架
Graefe, Revolution in Database Technology 等 LSM 综述 多篇;见 storage/31 把 LSM 与 B-Tree 并列为 访问方法 设计空间

B-Tree 侧的经典参照是 Bayer & McCreight (1972) 与 System R 的索引结构(postgresql-kernel 系列从 B-Tree 页讲)。不是「B-Tree 过时、LSM 全面更好」——O’Neil 论文针对的是 写比例高、读可接受多路归并 的工作负载;OLTP 点查 + 复杂事务仍以 B-Tree 为主流。

2.2 生产分叉:LevelDB → RocksDB

阶段 系统 相对前一代改了什么
2011 LevelDB(Dean & Ghemawat, Google) 单进程 leveled compaction、SST + MANIFEST;单后台 compaction 线程第 2 篇
2012+ RocksDB(Facebook fork) 多线程 flush/compaction、ColumnFamily、并发 MemTable 写、RateLimiter、Direct I/O(第 3 篇
2014+ bLSM 等工业变体 论文 Many-core compaction(Sears & Ramanan, SIGMOD 2012)影响「stall 与并发 compaction」思路;RocksDB 实现路径以 Wiki/源码为准,不一一对应

LevelDB 的 doc/impl.md 与 RocksDB Wiki RocksDB vs LevelDBA 级 工程 diff 文档;本系列第 2–3 篇按源码钉住差异,不在此重复 API 列表。

2.3 争论:Leveled vs Tiering(Size-Tiered)

Leveled(LevelDB/RocksDB 默认)与 tiering(Size-tiered / 大层合并)在文献与 storage/31 中已有 \(WA\)/\(RA\) 公式对照。顶会侧的代表性讨论:

立场 代表文献 核心主张
Leveled 读友好 LevelDB 设计;Dayan & Idreos, Dostoevsky (SIGMOD 2018) 控制 sorted run 数;L1+ 不重叠使点查每层 O(1) 个 SST;\(WA\) 随 size ratio \(T\) 与层数上升
Tiering 写友好 Cassandra / HBase 传统 STCS;Dayan & Idreos, The Log-Structured Merge-Bush (SIGMOD 2017) 同层多 run,减少层间反复重写;更低 \(WA\),但 run 数多 → \(RA\) 上升
中间地带 Dostoevsky;RocksDB Universal compaction 在 leveled/tiering 间插值;生产用 CompactionStyle::kCompactionStyleUniversal第 11 篇

工程结论(有文献支撑,非口号):没有 universally optimal 的 compaction;Flink state、TiKV 等 写突发 + 读 state 混合负载,常在 leveled 与 universal 之间 POC(参数见 storage/32,机制见本系列 10–12 篇)。

2.4 RUM 与「三种放大」

Data et al., RUM Conjecture (SIGMOD 2016) 把读开销、更新开销、内存(或空间)表述为 三选二 的不可能三角,与 LSM 社区的 \(WA\)/\(RA\)/\(SA\) 口径不同但 直觉同构:LSM 用 空间与读写吞吐。读 LSM 论文或调 compaction 时,应同时看 storage/31 第一节 的公式与 RUM 表述,避免把「放大」当成纯经验参数。

2.5 工程间隙:论文实验 vs 嵌入生产

论文/教程常见假设 TiKV / Flink 现实
单机 SSD、长时间 steady-state 云盘 IOPS 上限、checkpoint 突发读
均匀随机 key KeyGroup 倾斜、Region 热点
单 CF、单进程 多 CF、JVM + JNI + 多 subtask 目录
忽略 tail latency barrier 对齐、metastore 与 state IO 叠加

本系列 第 16–17 篇 从内核侧写 stall 与排查; 替代 TiKV/Flink 端到端 SLA 论文或 benchmark(如 db-frontier/12 中的 HTAP 基准)。

2.6 开放问题(阅读本系列的锚点)

  1. 自适应 compaction:能否在 无离线 DBA 前提下,按 workload 在 leveled/tiering 间切换且保证 tail?Learned / 反馈控制 compaction 仍在研究线与工业实验(B 级:各厂商 blog;A 级:Neo/Bao 系思路可对照 db-frontier/03 的「反馈环」,但对象不同)。
  2. LSM 上的 HTAP:行存副本(TiKV)+ 列存副本(TiFlash)是否优于 统一 LSM?社区无单一答案——见 db-frontier/12第 18 篇
  3. Disaggregated storage:NVMe-oF / CXL 下 WAL 与 SST 是否仍应本地?NSDI/OSDI 近年 storage disaggregation 论文与 RocksDB Remote Compaction 等 产品方向 并存,版本边界以 release note 为准。

三、RocksDB 在 LSM 生态中的位置

LevelDB 是 教学级基线:单后台 compaction 线程、无 Column Family、API 极简。RocksDB 保留 LSM 骨架(WAL、MemTable、SSTable、MANIFEST、Leveled/Universal compaction),在并发、可观测性、嵌入特性上大幅扩展。下面四个生产系统说明「谁在什么层次嵌 RocksDB」。

flowchart LR
  subgraph embed["RocksDB Embedded In"]
    TIKV["TiKV<br/>Region + Raft"]
    FLINK["Flink<br/>Keyed State"]
    KAFKA["Kafka Streams<br/>Local State Dir"]
    CH["ClickHouse<br/>EmbeddedRocksDB"]
  end
  ROCKS["RocksDB Engine"]
  TIKV --> ROCKS
  FLINK --> ROCKS
  KAFKA --> ROCKS
  CH --> ROCKS
系统 RocksDB 角色 key 空间特点 与本系列相关篇目
TiKV 每个 Region 的本地 KV 引擎(Raft 之上) 带 MVCC 时间戳的内部 key 编码 第 13–14、16、18 篇
Flink 每个 subtask 独立 RocksDB 实例 KeyGroup 前缀 + 多 Column Family 第 5、13、16 篇;对读 stream/12
Kafka Streams 任务目录下 rocksdb 子目录 changelog topic 与 state store 映射 第 16 篇对照
ClickHouse EmbeddedRocksDB 等场景的字典/元数据 KV 与 MergeTree 主存储分工 第 18 篇选型地图

TiKV:分布式 KV 的本地 LSM 层

TiKV 在 Raft 复制的 单个 Region 内使用 RocksDB(或兼容 LSM 实现)持久化 key-value。分布式事务、Percolator 模型、Raft log 不在 RocksDB 内核系列展开——本系列只覆盖 单个 DB 实例 内的 WAL、MemTable、compaction、Column Family。读 TiKV 文档时,应把 RocksDB 看作 Region 本地的有序 KV 引擎,上层再叠 MVCC 与 coprocessor。

Flink:流状态的后端

stream-processing 第 12 篇 已说明:EmbeddedRocksDBStateBackend 为每个需要 KeyedState 的 subtask 打开独立 RocksDB,Column Family 对应不同 state 变量,KeyGroup 决定 key 前缀。增量 checkpoint 上传自上次 checkpoint 以来 新生成的 SST——这直接依赖 RocksDB 的 不可变 SST 文件 语义与 MANIFEST 版本管理。

本系列第 16 篇会从 内核侧 补全:为何 L0 堆积会拖慢 checkpoint 期间的读、为何 write_buffer_size 与 flush 线程数影响 barrier 对齐后的状态持久化延迟。不在此重复 Flink 运行时调度全文。

Kafka Streams 与 ClickHouse Embedded

Kafka Streams 将 state store 落在本地 RocksDB 目录,通过 changelog topic 做容错;机制上与 Flink 同属 嵌入 LSM + 外部日志复制,但 key 编码与 checkpoint 模型不同(第 16 篇用对照表收束)。

ClickHouse 主存储是 MergeTree(列存角),RocksDB 出现在 EmbeddedRocksDB 等 辅助 KV 场景——选型上属于「列存为主、点查 KV 为辅」,第 18 篇阅读地图会把它与纯 RocksDB 负载区分。


四、与相邻系列的分工边界

读者容易在三处内容之间来回跳转。下表明确 谁写什么,避免重复劳动。

话题 本系列(RocksDB 内核) 相邻系列
LSM 三种放大公式与直觉 引用,不重复推导 lsm-tree/01storage/31
200+ 参数调优模板 不展开 storage/32
亲手实现 WAL/MemTable/SST 对照引用 lsm-tree 全系列
Flink KeyGroup / 增量 checkpoint 作业侧 第 16 篇内核侧 stream/12–13
MPP 查询 spill 到磁盘 不展开 query-engine/16
HTAP / TiKV Raft 全文 第 18 篇续作入口 db-frontier/12

与 lsm-tree DIY 的关系

lsm-tree 系列 用 C/Rust 从零实现可运行引擎,价值在于 亲手验证 InternalKey、VersionEdit、最小堆归并等设计。本系列假设读者已理解这些概念,转而阅读 RocksDB 9.x 源码 中的 DBImplFlushJobCompactionJobSuperVersion

对照阅读建议:lsm-tree 第 2 篇(WAL + MemTable)↔︎ 本系列第 4–5 篇;lsm-tree 第 3–4 篇(SST + Compaction)↔︎ 本系列第 6、10–11 篇。

与 storage/31–32 的关系

storage/31 从工程角度讨论 leveled/universal 策略与 Bloom 分配;storage/32 给出面向生产的 Options 模板。本系列 不讲参数 cookbook,而是讲 机制如何驱动 stall 与放大(第 10–12、17 篇),使读者在读 storage/32 时能对应到 DBImpl::WriteImpl 里的 WriteThreadCompactionPicker

与 stream-processing/12 的关系

stream/12 回答「Flink 作业里 RocksDB 目录长什么样、checkpoint 传什么」。本系列回答「传下来的 SST 在引擎里如何产生、compaction 如何与前台写争抢 I/O」。两篇 正交:stream/12 偏作业运维,本系列偏内核路径。


五、本系列 18 篇阅读地图

全系列按 基线 → 写路径 → 读路径 → compaction → 高级特性 → 生产嵌入 组织。依赖关系如下(节点内为篇号简称):

flowchart TD
  A["01 LSM Ecosystem"] --> B["02 LevelDB Baseline"]
  B --> C["03 RocksDB Architecture"]
  C --> D["04 WAL WriteBatch"]
  D --> E["05 MemTable Flush"]
  E --> F["06 SST MANIFEST"]
  F --> G["07 Get Snapshot"]
  G --> H["08 Iterator Merge"]
  H --> I["09 Cache Bloom"]
  I --> J["10 Leveled Compaction"]
  J --> K["11 Universal FIFO Stall"]
  K --> L["12 Concurrent RateLimit"]
  L --> M["13 ColumnFamily"]
  M --> N["14 Transactions"]
  N --> O["15 Checkpoint Ingest"]
  O --> P["16 Embedded Production"]
  P --> Q["17 Troubleshooting"]
  Q --> R["18 Engine Selection"]

推荐阅读路径

路径 篇目顺序 适合谁
存储三角闭合 1 → 4 → 7 → 10 → 13 → 18 从 PG/列存转 LSM
从 lsm-tree DIY 来 2 → 3 → 6 → 10 → 17 对照 toy vs 生产
从 Flink/stream 来 1 → 5 → 9 → 11 → 16 → 17 排查 state 慢
Compaction 调优 10 → 11 → 12 → 17 平台 / DBA
完整通读 1 → … → 18 系统掌握

各部分一句话价值

第一部分(01–03):生态定位、LevelDB 单线程骨架、RocksDB fork 后的 diff 地图——建立后续读源码的坐标系。

第二部分(04–06)WriteBatch 如何原子提交、MemTable 如何 flush 成 L0 SST、MANIFEST 如何描述 LSM 快照——写路径.disk 语义。

第三部分(07–09)Get 与 Snapshot 的 sequence 边界、Iterator 多路归并、tombstone 可见性、Block Cache 与 Bloom 如何裁剪读放大。

第四部分(10–12):Leveled 不变式、Universal/FIFO、write stall 状态机、并发 compaction 与 RateLimiter——与 storage/31 公式呼应的源码侧。

第五部分(13–15):Column Family 共享 WAL、事务 OCC/WritePrepared 边界、Checkpoint 与 External Ingest——嵌入与运维接口。

第六部分(16–18):Flink/TiKV/Kafka Streams 对照、LOG/GetProperty 排查、RocksDB vs InnoDB vs 列存选型地图。


六、六个关键问题(系列贯穿)

系列 index 归纳的六个问题,本篇先给出索引,后续篇章逐条钉源码:

  1. LevelDB 瓶颈在哪,RocksDB 改了什么? → 第 2–3 篇(单线程 vs 线程池、WriteThread、Column Family)。
  2. Get / Iterator 如何从 MemTable 追到 Ln? → 第 4–9 篇(SuperVersionMergeIterator)。
  3. Compaction 如何驱动 write stall 与三种放大? → 第 10–12 篇(CompactionPickerStopWritesTrigger)。
  4. Column Family 与 WAL 如何共享又隔离? → 第 13 篇(ColumnFamilyData)。
  5. Flink / TiKV 如何映射到 CF 与 key? → 第 16 篇(对读 stream/12)。
  6. 写 stall、空间膨胀、读放大如何诊断? → 第 17–18 篇(Wiki Troubleshooting + 选型)。

七、实验与复现入口

本系列性能相关实验统一放在 post/db/rocksdb/reproduce/,脚本包括 run_db_bench.shrun_properties.sh。环境为 WSL2 上编译 RocksDB 9.x 的 db_bench未在本篇执行的 benchmark 不写入正文结论

第 4 篇起会给出 WAL 目录列表、flush 前后 SST 数量、rocksdb.stats 属性等可复现步骤。Flink 集群相关指标(checkpoint 耗时、状态体积)仅在读者自有环境验证,系列规划不伪造 Web UI 数字(见 PLAN.md 实验台账)。


八、小结

存储引擎三角中,RocksDB 占据 写优化 LSM 一角:前台顺序写、后台 compaction 换写吞吐,代价是读放大与写放大需要在生产里持续调参。它与 PostgreSQL B-Tree、列存 scan 不是替代关系,而是 负载分工——TiKV、Flink、Kafka Streams 把 RocksDB 嵌在各自分布式或流式栈的 本地持久化层

本系列在 lsm-tree DIYstorage/31–32stream/12 已覆盖的内容之外,按 RocksDB 9.x 源码补全 写路径 → 读路径 → compaction → 嵌入 → 排查 的内核叙事。下一篇进入 LevelDB 1.23 基线:单线程 leveled compaction 与 DBImpl::Write/Get 的最小骨架,为第 3 篇的 diff 地图提供对照尺。


参考资料

  1. O’Neil, Patrick E. et al., The Log-Structured Merge-Tree (LSM-Tree), Acta Informatica, 1996.
  2. Dayan, Niv & Idreos, Stratos, Dostoevsky: Better Space-Time Trade-offs for LSM-Tree Based Key-Value Stores, SIGMOD 2018(leveled vs tiering 分析框架)。
  3. Dayan, Niv & Idreos, Stratos, The Log-Structured Merge-Bush & the Wacky Continuum, SIGMOD 2017(tiering 与 merge bush 模型)。
  4. Data, R. et al., RUM Conjecture (SIGMOD 2016)(读/更新/内存权衡;与 WA/RA/SA 对照阅读)。
  5. Sears, Ryan & Ramanan, Rahul, bLSM: A General Purpose Log Structured Merge Tree, SIGMOD 2012(多核 compaction 与 stall 思路;工业对照)。
  6. Dean, Jeff & Ghemawat, Sanjay, LevelDB(google/leveldb README / doc/impl.md)。
  7. RocksDB Wiki, Architecture / Overview / RocksDB vs LevelDB
  8. Boncz, Peter et al., MonetDB/X100: Hyper-Pipelining Query Execution, CIDR 2005(列存 scan 对照;见 columnar-engine)。
  9. Apache Flink / TiKV 官方文档(嵌入边界;A/B 级)。
  10. db-frontier/12 HTAP(HTAP 开放问题;B 级接口)。
  11. 本系列 indexstream/12storage/31–32

返回 系列目录 | 下一篇:LevelDB 基线:单线程 Leveled 与 DBImpl 骨架

同主题继续阅读

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

2026-07-07 · database / storage

【RocksDB 内核机制】选型与存储栈阅读地图

用决策树收束 RocksDB 与 InnoDB、列存、湖仓的适用边界;给出存储引擎三角 + 数据平台全栈阅读地图,对接 query-engine/18 与 postgresql-kernel,并标注 HTAP/TiKV 续作入口。

2026-04-22 · db / storage

数据库内核实验索引

汇总本站数据库内核文章:PostgreSQL / MySQL InnoDB / 列存引擎 / 数据湖与开放表格式 / 流式数据处理 / 分布式 OLAP 查询引擎 / RocksDB 内核,以及 LSM-Tree 从零实现实验。


By .