一句话总结:
B+ Tree = 面向读优化的“磁盘随机读友好型”索引结构。 LSM-Tree = 面向写优化的“磁盘顺序写友好型”日志结构合并树。
它们最核心的哲学差异是:
-
B+ Tree: 减少磁盘随机 IO(适合读多写少)
-
LSM-Tree: 减少写放大、变随机写为顺序写(适合写多读少、写吞吐大)
下面从结构、写入路径、查询路径、磁盘布局、适配场景等维度做深度拆解。
| 项 | B+ Tree | LSM-Tree |
|---|---|---|
| 设计初衷 | 快速查找,减少磁盘随机 IO | 提高写入吞吐,顺序写替代随机写 |
| 适配介质 | HDD(机械盘随机读慢) | HDD / SSD(顺序写极快) |
| 推荐场景 | MySQL (OLTP)、KV 读多场景 | RocksDB、LevelDB、高写入量 KV 系统 |
核心区别:
B+ Tree:重点优化“读”,代价是“写成本高(随机 IO)”。 LSM-Tree:重点优化“写”,代价是“读放大、空间放大、合并成本”。
- 每个节点存 key(有序)、子节点指针
- 数据节点按叶子链表顺序排列
- 查找为 O(log N)
- 插入删除需要在树上随机读写磁盘页
其核心是:
节点页在磁盘中随机散布,“一次 IO = 读一个页” → 随机 IO 很多。
- 写操作先写 WAL(顺序写)
- 再写 MemTable(跳表 / 红黑树)
- 内存满后 flush 成 SSTable(顺序写、不可修改)
- 后台 compaction 合并 SST 层级
核心是:
所有写都是顺序写,没有随机写。
LSM 是典型的“写优先”结构。
写一次需要:
- 找到目标页(随机 IO)
- 在页内插入/更新 key(页内移动数据)
- 若页满,分裂(多次随机 IO)
代价:
- 随机写 IO 非常贵(尤其机械盘)
- 写放大高(一次写可能变成多次页分裂)
写一次:
- 写 WAL(顺序 IO)
- 写 MemTable(内存)
- MemTable 满 → Flush 成 SST(大块顺序写)
- 后台 Compaction(顺序读+写)
代价:
- 写清晰可控
- 磁盘只做顺序写,吞吐极高
难点:
Compaction 会产生写放大,严重时写放大可达几倍甚至十几倍。
总结:
- B+ Tree 写 = 随机写
- LSM 写 = 顺序写
Root → Child → Leaf → Data
- logN 次随机读
- 不需要扫描多个文件
- 查询快、稳定
读的时候需要查多个层级:
MemTable → Immutable MemTable → Level0 → Level1 → Level2 ...
因此会出现:
- 读放大(Read Amplification)
- 若无 Bloom Filter,可能多个 SST 都要查
为减少读放大,LSM 引入:
- Bloom Filter
- Block Cache
- Key index/cache
- 更复杂的 compaction 策略
所以:
LSM = 写快但读慢 B+Tree = 写慢但读稳定
- 节点分裂导致额外空间
- 但总体空间放大不大(常见 < 2)
- 多层 SST 同时存在
- Compaction 前旧文件和新文件并存
- 删除、更新要等 compaction 回收
LSM 空间放大显著:
RocksDB 典型空间放大:1.5~3倍
读写都是直接访问页,不依赖后台任务:
- 延迟稳定
- P99/P999 平滑
- MySQL 适合强一致性要求的 OLTP
Compaction 会导致:
- 磁盘 IO 飙升
- 用户读写延迟陡升
- Worst Case 极端抖动
所以 TiKV/RocksDB 方案需要非常多的资源隔离。
| 场景 | 推荐结构 | 原因 |
|---|---|---|
| 即时读多写少(OLTP) | B+ Tree | 读稳定、低延迟、随机写少 |
| 写多读少、吞吐极大 | LSM | 写吞吐强、顺序写优秀 |
| SSD 集群、大规模 KV | LSM | SSD 特性决定顺序写更快 |
| 金融、电商事务 | B+ Tree | P99 延迟更稳定、读取更快 |
| 日志、监控、时序数据 | LSM | 写多、批量写、压缩好 |
你可以这样回答面试官:
“B+ Tree 和 LSM-Tree 的本质区别在于它们的设计哲学完全不同:
B+ Tree 是面向读优化的结构,通过减少随机读实现稳定的读性能; LSM-Tree 是面向写优化的结构,通过顺序写与分层 SSTable 来获得高写入吞吐。
在写路径上:
- B+ Tree 需要随机 IO、页分裂,写放大高
- LSM 只做顺序写,写非常快
在读路径上:
- B+ Tree 读一次命中,延迟稳定
- LSM 要查多个层级,读放大明显
代价方面:
- B+ Tree 空间放大低
- LSM 空间放大高,还会引入 compaction 导致延迟抖动
B+ Tree 更适合读多写少、低延迟的 OLTP 场景(如 MySQL), LSM 更适合写密集型、高吞吐的 KV 系统(如 RocksDB、LevelDB、TiKV)。”