第 4 章 · 存储引擎与数据系统

“这一章学完,你能用 Go 写一个分布式搜索引擎——不是用 ES,是你自己写。“


本章覆盖的课程

编号课程本章的产出
037MySQL 实战45讲B+Tree 存储引擎 + WAL + MVCC + 事务
117Redis 核心技术与实战内存数据结构 + 持久化 + 主从复制

为什么这一章最”值钱”

前 3 章是”内功”,这一章是”外功”——学完你手上就有了能变现的东西。

具体来说,学完这一章你会拥有:

  1. 一个 B+Tree 存储引擎:支持持久化、崩溃恢复、范围查询
  2. 一套 WAL + MVCC 实现:支持事务和并发读写
  3. 一个 LSM-Tree 引擎:写优化,适合时序和日志场景
  4. 一个倒排索引模块:全文检索的核心
  5. 一个分布式 KV 存储的骨架:分片 + 复制(第 5 章补上共识)

这五样东西,组合起来就是一个简化版的 Elasticsearch + Redis。

这就是你在开篇说的:“没有 ES 可以创建自己的 ES”。这一章就是兑现这句话的地方。


章节结构

文档内容动手项目
00-设计推演:从零造一个存储引擎.md★ 必读开篇:从内存 map 到 LSM / B+Tree 的八次失败与演进—
01-B+Tree 存储引擎:从内存到磁盘.md页管理、B+Tree 落盘、Buffer Pool、Slotted Page磁盘版 B+Tree
02-WAL、MVCC 与事务.md崩溃恢复、隔离级别、undo log、MVCC 实现WAL + MVCC 引擎
03-动手:从零构建分布式搜索引擎.mdLSM-Tree、倒排索引、分片、压缩自己的 ES

★ 阅读顺序提醒:先读 00-设计推演。它不讲”怎么做”,讲”为什么非得这么做”—— 从 map[string]string 出发,每前进一步都先遇到一个具体失败,再由失败逼出下一个设计。

如果你只想从这一章拿走一样东西,拿走这一篇。实现会过时,推导链不会。


学完这一章,你应该能

  1. 解释为什么 B+Tree 的节点大小 = 磁盘页大小,并实现页管理
  2. 用 Go 实现一个磁盘版 B+Tree,支持持久化、分裂、范围查询
  3. 解释 WAL 的三种刷盘策略,以及它们对持久性和性能的影响
  4. 实现 MVCC:快照读、版本链、ReadView
  5. 解释 MySQL 的四个隔离级别各自怎么实现,以及各自的代价
  6. 用 Go 实现一个 LSM-Tree 引擎,含 MemTable、SSTable、Compaction
  7. 用 Go 实现一个倒排索引,支持全文检索和打分
  8. 设计一个分布式搜索引擎的分片策略

贯穿全章的核心问题

问题 1:持久化的本质是什么?

四个层次:

① 什么都没做
   写内存就返回 → 断电数据丢失

② write() 系统调用
   写到内核的 page cache → 断电还是丢(进程崩溃不丢)

③ fsync()
   刷到磁盘 → 断电不丢(但磁盘可能有自己的缓存,需要 FUA)

④ 复制
   写到多台机器 → 单机故障不丢

每一层都在用性能换持久性。 这一章你会看到 MySQL、Redis、RocksDB 各自选了哪一层,以及为什么。

问题 2:并发读写的本质是什么?

三种方案:

方案思路代价
锁读加锁,写加锁,互斥并发度低
MVCC每个事务看到”快照”,读写不冲突存储多版本,需要 GC
串行化所有操作排队最简单,但吞吐低

MVCC 是现代数据库的标准答案。 它的核心洞察是:

“读”和”写”的冲突,可以通过”给每个读者一个快照”来消除。

代价是要存多个版本,并且要有机制回收旧版本。

问题 3:索引的本质是什么?

索引回答的是”怎么少读数据”。

无索引:  扫全表 → O(N) 次磁盘 IO
B+Tree:  树高 3-4 → O(log N) 次 IO(16 亿数据只要 3 次)
哈希:    1 次 IO,但不支持范围查询
布隆过滤器: 0 次 IO(如果确定不存在)

每一种索引结构,都是”查询能力”和”维护成本”的权衡。


一个重要的提醒

这一章的代码量是全书最大的。如果你只读不写,收获会非常有限。

建议的仓库结构:

~/gohard/ch04-storage/
├── btree/          B+Tree 存储引擎
├── wal/            WAL 实现
├── mvcc/           MVCC 引擎
├── lsm/            LSM-Tree 引擎
├── index/          倒排索引
└── search/         完整的分布式搜索引擎

每读完一节,把对应的代码跑起来,加几个 print 看看中间状态。


开始阅读:00-设计推演:从零造一个存储引擎