存储基础(3):LSM、Compaction 与写放大
发布于:
本文是「存储基础系列」的第 3 篇,建立一个 LSM 的“直觉模型”,并解释 compaction 为什么既是性能来源,也是写放大的主要来源。
1. LSM 是什么(用一句话)
LSM(Log-Structured Merge-Tree)核心思路:
- 写入先顺序写(落在内存/日志里,最终顺序写成 SST 文件)
- 后台再把多个有序文件合并(compaction),维持读性能与空间回收
它特别适合“写多读少 / 随机写多”的场景,因为随机写变成顺序写。
2. SST、Level、Compaction:三件套
一个典型实现会有:
- memtable:内存中的有序结构(跳表/红黑树等)
- WAL:写前日志(保证崩溃恢复)
- SST(Sorted String Table):磁盘上的有序文件
- Levels:多层文件组织,每层有自己的大小上限
- Compaction:把“上层的小文件”合并进“下层的大文件”
2.1 L0 为什么经常是“抖动之源”
很多 LSM 实现里,L0 的特点是:文件之间允许重叠。这带来两个直接后果:
- 读放大更敏感:一次点查可能需要查多个 L0 文件(尤其在写高峰、flush 很频繁时)。
- 写停顿更常见:当 L0 文件数/大小超过阈值,系统通常会触发 stall/throttle,逼迫后台 compaction 追账。
所以线上常见的“写入突然变慢/读突然变慢”,第一反应就是:看看 L0 是否堆积。
3. 写放大从哪来
写放大(Write Amplification)可以粗略理解为:
应用写入 1 byte,系统实际写入了 N byte
主要来源:
- Compaction 重写数据:同一条记录会在多个层级反复被合并、重写。
- 删除与覆盖:tombstone/旧版本需要通过 compaction 才能真正回收。
- 校验、索引、元数据:除了 data,本身还有 index/filter 等结构写入。
3.1 Leveled vs Tiered:写放大、读放大、空间放大的三角
LSM 里最关键的策略分歧之一是“合并的积极程度”:
- Leveled:每层 key-range(尽量)不重叠,读路径更可控,但会更频繁地把上层数据合并进下层 → WA 往往更高。
- Tiered / Universal:更像“攒一批再合并”,同一条数据被重写次数更少 → WA 往往更低,但读要合并更多 run,且空间/版本更难控。
工程上通常不是非黑即白:很多系统会对不同层采用不同策略(例如 L0 特殊处理,底层更“稳”)。
一个很直觉的结论:
- 层级越深、合并越频繁,写放大越大
- 但如果层级不深或不合并,读放大就会上升
这就是 LSM 的经典权衡:读放大 vs 写放大 vs 空间放大。
4. Compaction 为什么会让系统“抖”
可能见过这类现象:
- 平时写入很稳
- 一到 compaction 高峰,写延迟突然抬头、抖动明显
原因通常是资源竞争:
- IO 竞争:compaction 读写吞吐吃掉了磁盘带宽
- CPU 竞争:压缩/解压、校验、排序合并消耗 CPU
- 内存压力:compaction buffer、block cache、memtable 之间互相挤占
4.1 会在监控里看到哪些“前兆”
这类问题最怕“等到写延迟爆炸才发现”。更有效的是盯前兆:
- L0 文件数/大小 持续上升
- pending compaction bytes 持续上升
- write stall / throttle 次数上升
- 读延迟 同步上升(读要查更多层/更多文件)
4.2 Compaction 的本质:把“历史债务”摊到现在
写入时你享受了顺序写(WAL+MemTable+Flush)的红利,但 compaction 会在后台把“多版本、重叠文件、删除标记”清理掉。
因此 compaction 更像“还债”:
- 欠债少:系统稳
- 欠债多:迟早要还(要么后台抢资源导致抖动,要么前台 stall 被迫降速)
5. 一个“不会错”的调优方向
不讨论具体系统参数,给一个通用的方向:
- 把 compaction 的 IO/CPU 预算做成可控的“后台配额”
- 把用户写入的尾延迟保护起来
对应的工程手段通常是:
- 限制 compaction 并发(线程数/IO)
- 分层/分速率的 compaction 策略(不同层不同优先级)
- 写入节流(当 compaction backlog 过大时保护系统稳定)
5.1 正确姿势:把后台资源当成预算
建议把 compaction 当成“后台作业系统”:
- 给它明确的 IO/CPU 预算(并发、速率、优先级)
- 给前台写入明确的 尾延迟保护策略(stall/throttle 阈值)
这样系统在高峰期会更“可解释”,而不是靠运气。
下一篇我们会把“崩溃恢复”补齐:WAL、checkpoint、以及为什么很多系统读写路径都绕不开日志。