Apache License 2.0

技术解读

以下内容由 GitRead 中文社区 编辑团队基于项目 README 与官方资料整理, 结合中文开发者的常见使用场景补充技术选型建议。具体以项目官方文档为准。

在 Rust 里重建一颗 LSM-Tree:skyzh/mini-lsm 深度拆解

项目背景与痛点

LSM-Tree 是当今写密集型存储的事实标准:RocksDB、LevelDB、TiKV、Cassandra、ScyllaDB,甚至 ClickHouse 的 MergeTree 家族,骨子里都是同一套思路——把随机写变成顺序写,用后台合并换取写入吞吐。

问题在于,理解它很难。论文只讲清了「为什么」,而从「为什么」到「能跑起来的代码」,中间隔着 WAL 的 fsync 语义、Block 的前缀压缩、Manifest 的原子切换、Compaction 的层级调度。直接读 RocksDB 源码?几十万行代码、线程池、内存分配器、版本管理(VersionEdit / SuperVersion)会迅速淹没你。读 MIT 6.824 的 SimpleDB 或者各种「200 行实现一个 KV」的博客?又太浅,浅到根本不涉及崩溃恢复和 MVCC——而这恰恰是工程实现中最容易出错的部分。

mini-lsm 的价值就在于它卡在中间的生态位:它不是一份可用的数据库,而是一条可执行的、有测试兜底的学习路径。作者 skyzh 把 LevelDB/RocksDB 的核心机制拆成三周、每天一个主题,配合完整的测试套件,让一个人真的能在两三周内亲手写出一颗能通过崩溃一致性测试的 LSM 引擎。

核心技术揭秘

整个引擎的分层非常克制,几乎每个模块都能对应到工业实现中的同名组件。

graph TD A[get / put / delete 接口] --> B[LsmStorageInner 事务层] B --> C[写路径:WAL 追加] C --> D[MemTable 插入,Key 携带时间戳] D --> E{"MemTable 是否超阈值?"} E -->|"是"| F[冻结并 Flush 为 L0 SST] E -->|"否"| G[写事务提交返回] F --> H[Manifest 持久化层级状态] H --> I{"触发 Compaction?"} I -->|"Leveled 策略"| J[逐层按 size ratio 合并] I -->|"Tiered 策略"| K[同层 run 归并] J --> H K --> H B --> L[读路径:MemTable 与各层 SST 归并] L --> M[MergeIterator 与 TwoMergeIterator] M --> N[Block 解码,Bloom Filter 剪枝] N --> O[按时间戳判定可见性]

第一层:写路径与崩溃恢复。 每次 put 先追加 WAL,再写内存中的 MemTable(可选 crossbeam-skiplist 或 BTreeMap)。MemTable 超过阈值就冻结、刷成 L0 的 SST 文件,并在 Manifest 里记一笔。这里有一个容易被忽略的设计:Manifest 用 JSON 记录 SST 列表与层级状态,本身不做增量日志,而是整体覆盖写——教学场景下足够,但也恰好暴露了真实引擎为什么要用 VersionEdit + 原子 rename。

第二层:Key 编码与 MVCC。 mini-lsm 的第三周是精华。用户 key 与 u64 时间戳拼接,时间戳按降序编码(u64::MAX - ts 大端序),这样同一个 key 的所有版本在 SST 里天然按「新→旧」排列,迭代器扫到的第一个可见版本就是答案。删除被表达为墓碑记录,配合 watermark 判定快照可见性,一套机制同时解决点查、范围扫描和多版本读。这个技巧在 TiKV 和 RocksDB 里叫「user key + seq」,mini-lsm 把它讲透了。

第三层:迭代器的组合式抽象。 这是全项目最漂亮的部分。MergeIterator 用二叉堆做多路归并,TwoMergeIterator 把「MemTable 流」和「层内流」串成一条有序流,再套一层过滤可见性的 LsmIterator。所有迭代器统一实现 DoubleEndedIterator,于是 scan 天然支持正向与反向遍历,seek_to_key 也只是在每层做一次定位。Rust 的 trait object 加泛型在这里不是炫技,而是让「加一层新语义」的成本降到几十行。

第四层:SST 的细节工程。 Block 内部对相邻 key 做前缀压缩,每 16 个 key 设一个 restart point,查找时先二分 restart points 再线性扫;块尾附 CRC32 校验。点查走 Bloom Filter 直接跳过整个 SST,范围扫描则依赖 block cache 减少解码开销。这些细节决定了引擎是「玩具」还是「像样」。

功能亮点与差异

相比同类的教学项目,mini-lsm 有三处明显不同。

一是测试驱动到近乎偏执。 每个 day 的骨架代码旁边都摆着对应的集成测试,从 MVCC 可见性到 compaction 后的数据完整性,全部可验证。你不是「写完祈祷它能跑」,而是「看着红变绿」。

二是故障注入模拟器。 仓库提供了可对文件系统注入截断、半写、断电等故障的模拟层,用来验证 WAL 重放与 Manifest 恢复的正确性。绝大多数「手写 KV」教程完全没有这一环,而这恰恰是存储引擎最贵的部分。

三是策略可选。 第二周同时实现 Simple Leveled、Tiered(Universal)和基于 size ratio 的 Leveled Compaction,能直接对比写放大与读放大的权衡——这种「一次实现三种策略」的收益,在真实项目里几乎不可能获得。

应用场景与落地建议

不要用于生产。 缺后台线程调度、Compaction 限速、WAL 复用与周期性清理、SST 生命周期管理,错误处理也偏教学化。Arc<RwLock<State>> 的粗粒度锁在高并发下会迅速成为瓶颈。

适合三种用途: 团队内部的存储内核培训;为嵌入式 KV 或本地索引做原型验证;把你自己的 compaction 策略丢进它的模拟器里做对照实验。

真要基于它改造,优先级排序是:先把 WAL 的 fsync 与批量提交做对,再把 compaction 挪到独立线程并加限速,然后处理读路径与 compaction 的锁竞争,最后补上块级索引和更细的缓存淘汰策略。

综合评价

优点很清楚:路径设计有教学法,模块边界干净,Rust 的迭代器抽象把 LSM 的多路归并表达得极为优雅,模拟器是同类项目中罕见的加分项。它让你写完的不是「能存 key 的玩具」,而是「能被测试证明正确的引擎」。

短板同样明显:这是教学实现,性能调优、并发细节、运维能力全部留白;部分 day 的骨架需要你自己补全,MVCC 与 compaction 两章对没接触过存储系统的读者是陡坡。

一句话总结:如果你想真正搞懂 LSM-Tree 里那些「论文不会写、源码读不动」的部分,mini-lsm 目前是中文圈之外最好的一条路。

关于

GitHub 原始描述(英文)

learn database internals by building a storage engine in Rust

描述取自 GitHub 官方 API,保持仓库作者书写的原文。

简要分析

热度分
5561
价值分
2727
活跃状态
活跃

语言占比

CSS
Handlebars
Rust
Shell

Release

README

Mini-LSM: Learn database internals by building an LSM storage engine in Rust

Mini-LSM: Build a Database Storage Engine in Rust

CI (main)

Mini-LSM is a hands-on course in database internals for systems and backend engineers. Build an LSM-tree storage engine from memtables and SSTs through compaction, crash recovery, MVCC, and transactions.

Start the guided three-week course · Try the coding-agent track

Week 1 produces a working storage engine. Weeks 2 and 3 add production-inspired compaction, durability, concurrency control, and multi-version transactions.

What You Will Build

Mini-LSM course roadmap

By the end of the course, your engine will include:

  • an ordered in-memory write buffer and immutable SST files;
  • point lookups and range scans over merged memory and disk state;
  • multiple compaction strategies and background maintenance;
  • a manifest and write-ahead log for crash recovery; and
  • snapshots, MVCC garbage collection, optimistic concurrency control, and serializable validation for tracked keys.

Mini-LSM focuses on the storage layer of a database. It does not cover SQL parsing, query optimization, replication, or distributed consensus.

Choose a Learning Path

Path Best for Format Status
Guided course Learners who want to implement and reason through each subsystem Three weeks, with seven chapters per week Complete
Coding-agent track Learners who want an agent to handle mechanical implementation without outsourcing system design Three planned days built around decision stops, focused code slices, and adversarial tests Days 1–3 available

You need basic Rust, but you do not need prior knowledge of LSM trees, compaction, MVCC, or transaction isolation. The course is a good fit if you have used systems such as PostgreSQL, MySQL, Redis, or RocksDB and want to understand what happens below their APIs.

Community

You may join skyzh’s Discord server and study with the mini-lsm community.

Join skyzh's Discord Server

Add Your Solution

If you finished at least one full week of this course, you can add your solution to the community solution list at SOLUTIONS.md by submitting a pull request.

Development

For Students

You should modify code in mini-lsm-starter directory.

cargo x install-tools
cargo x copy-test --week 1 --day 1
cargo x scheck
cargo run --bin mini-lsm-cli
cargo run --bin compaction-simulator

For Course Developers

You should modify mini-lsm and mini-lsm-mvcc

cargo x install-tools
cargo x check
cargo x book

If you changed public API in the reference solution, you might also need to synchronize it to the starter crate. To do this, use cargo x sync.

Code Structure

  • mini-lsm: the final solution code for <= week 2
  • mini-lsm-mvcc: the final solution code for week 3 MVCC
  • mini-lsm-starter: the starter code
  • mini-lsm-book: the course

We have another repo mini-lsm-solution-checkpoint at https://github.com/skyzh/mini-lsm-solution-checkpoint. In this repo, each commit corresponds to a chapter in the course. We will not update the solution checkpoint very often.

Demo

You can run the reference solution by yourself to gain an overview of the system before you start.

cargo run --bin mini-lsm-cli-ref
cargo run --bin mini-lsm-cli-mvcc-ref

And we have a compaction simulator to experiment with your compaction algorithm implementation,

cargo run --bin compaction-simulator-ref
cargo run --bin compaction-simulator-mvcc-ref

Guided Course Structure

The guided course has three complete weeks.

  • Week 1: Storage Format + Engine Skeleton
  • Week 2: Compaction and Persistence
  • Week 3: Multi-Version Concurrency Control
  • The Rest of Your Life: Closing chapter with expansion paths
Week + Chapter Topic
1.1 Memtable
1.2 Merge Iterator
1.3 Block
1.4 Sorted String Table (SST)
1.5 Read Path
1.6 Write Path
1.7 SST Optimizations: Prefix Key Encoding + Bloom Filters
2.1 Compaction Implementation
2.2 Simple Compaction Strategy (Traditional Leveled Compaction)
2.3 Tiered Compaction Strategy (RocksDB Universal Compaction)
2.4 Leveled Compaction Strategy (RocksDB Leveled Compaction)
2.5 Manifest
2.6 Write-Ahead Log (WAL)
2.7 Batch Write and Checksums
3.1 Timestamp Key Encoding
3.2 Snapshot Read - Memtables and Timestamps
3.3 Snapshot Read - Transaction API
3.4 Watermark and Garbage Collection
3.5 Transactions and Optimistic Concurrency Control
3.6 Serializable Snapshot Isolation
3.7 Compaction Filters

mini-lsm inspired several projects used in production.

  • SlateDB is an LSM engine over the object storage system.
  • Tonbo stores parquet files directly on the object storage and organizes them in an LSM tree structure.

License

The Mini-LSM starter code and solution are under Apache 2.0 license. The author reserves the full copyright of the course materials (markdown files and figures).

评论

暂无评论