LSM Tree 为什么写入快?写放大从哪里来?
简化版
LSM Tree 把随机写转成 WAL 顺序追加和内存表更新,内存表满后再顺序刷成不可变 SSTable,所以前台写入快。代价是后台 Compaction 会反复读取、合并并重写旧 SSTable,同一份用户数据可能被写多次,形成写放大;同时还会带来读放大和空间放大。
详细版
写路径通常是:先追加 WAL 保证崩溃恢复,再写 MemTable;MemTable 达到阈值后冻结为 immutable memtable,后台顺序刷盘为 L0 SSTable。前台不需要每次在磁盘 B+Tree 中随机定位并原地修改。
Compaction 为了维持有序、清理旧版本和墓碑,会把多个 SSTable 读出归并,再写入下一层。若 1 GB 用户数据在层级迁移中累计写盘 8 GB,写放大约为 8。
分层 Compaction 通常读放大较低、空间较稳,但写放大偏高;分级/size-tiered Compaction 写入更友好,却可能保留更多重叠文件,增加读放大和空间占用。
调优不能只追求最低写放大。要同时观察 WAL/MemTable、L0 文件数、Compaction backlog、磁盘带宽、stall 时间、读放大、空间放大和墓碑清理安全窗口。
完整版教学
一、为什么随机写昂贵
原地更新结构需要找到目标页并修改,可能触发随机 I/O、页分裂和多页写入。LSM Tree 先把变更变成追加日志和内存有序结构,批量积累后再生成顺序文件。它把前台随机工作推迟并合并到后台,因此提高吞吐,但没有让工作凭空消失。
Put(k,v) -> append WAL -> update MemTable -> ACK
MemTable full -> freeze -> flush sequential SSTable
二、一次写入的完整路径
WAL 提供持久性,MemTable 提供有序查询与批量缓冲。假设 MemTable 阈值为 256 MB,写满后生成约 256 MB SSTable;系统可以让新 MemTable 继续接收请求,后台刷旧表。若刷盘跟不上,immutable 队列和 L0 文件会增长,最终必须写停顿保护内存和磁盘。
| 阶段 | 主要动作 | 前台影响 |
|---|---|---|
| WAL | 顺序追加并按策略 fsync | 决定持久性延迟 |
| MemTable | 内存插入 | 通常很快 |
| Flush | 生成 L0 SSTable | 后台占用带宽 |
| Compaction | 归并并重写层级 | 可能与前台争抢 I/O |
三、写放大如何计算
写放大常用“实际写入存储介质的字节 / 用户逻辑写入字节”估算。用户更新 100 GB,但 WAL、Flush 和 Compaction 合计写了 900 GB,则总写放大约为 9;有的口径不计 WAL,所以比较系统前必须统一测量边界。
WA = physical_bytes_written / logical_user_bytes
SSD 的设备内部垃圾回收还有一层介质写放大,数据库 WA 与设备 WA 可能相乘,直接影响寿命和尾延迟。因此只看业务 QPS、不看物理写字节,会低估成本。
四、Compaction 为什么反复重写
新数据先进入上层,随后逐层下沉。为了保证每层大小和重叠范围满足策略,系统需要读取上层文件及其与下层重叠的文件,按 key 合并后写新文件,再原子替换元数据并删除旧文件。同一个 key 在 L0、L1、L2 迁移时会多次被写。
大小比为 10 的 leveled 结构中,理想模型下每层数据可能因上层数据逐批到来而多次参与归并。实际 WA 受层数、size ratio、文件选择、覆盖更新比例和 key 分布影响,不能仅凭一个固定公式断言。
五、三种放大的取舍
Leveled Compaction 控制每层 key 范围重叠,点查需要访问的文件少,空间也较紧凑,但重写次数较多。Size-tiered 把大小相近文件批量合并,单份数据重写次数可能更少,却保留更多重叠版本,读时要查更多文件。
Universal、FIFO 等策略适合不同负载。时序数据按 TTL 整文件淘汰时,FIFO 可能避免无谓归并;覆盖更新和强点查场景则更偏向 leveled。策略必须按读写比、保留期和存储介质选择。
六、如何避免 Compaction 拖垮前台
调度器要给 Flush 高于普通 Compaction 的优先级,避免 MemTable 无处刷盘;同时对后台 I/O 限速并按 backlog 动态提升额度。并发 Compaction 过多会占满磁盘队列,过少又会让 L0 堆积。
监控至少包括 L0 文件数、pending compaction bytes、flush/compaction throughput、write stall 秒数、P99、磁盘利用率和 tombstone age。容量必须预留临时输出空间,因为新文件发布前旧文件不能立即删除。
记忆钩子:LSM 不是“少写”,而是“前台先顺序写、后台再集中还债”;Compaction 就是还债过程,写放大是利息。
七、常见误区与追问
- 误区:LSM Tree 写入快是因为不写磁盘。 持久写仍需 WAL,后续还要 Flush 和 Compaction。
- 误区:写放大只计算 Compaction。 是否计 WAL、Flush 和设备层必须先统一口径。
- 误区:降低 Compaction 就一定更快。 backlog 会增加读放大、空间占用并最终触发写停顿。
- 追问:墓碑何时能删除? 只有确认所有可能含旧值的层和副本都越过安全边界后才能清理。
- 追问:leveled 与 size-tiered 怎么选? 前者偏读和空间,后者偏写,最终按工作负载压测。
- 追问:为什么 SSD 寿命受影响? 数据库重写与设备垃圾回收叠加,放大实际 NAND 写入量。
八、加强记忆
把 LSM Tree 记成“WAL 保命、MemTable 缓冲、SSTable 落盘、Compaction 整理”。写快来自把随机写转为追加和批量顺序写;写放大来自同一数据在整理和下沉中被反复重写。面试回答必须同时讲读、写、空间三种放大,以及 backlog、限速和写停顿这些工程后果。