RocksDB 笔记:Flush 触发条件:write buffer 与 L0 文件数量
发布于:
在 RocksDB 写入链路中,最容易遇到两类“看似突然”的现象:
- 写入延迟开始抖(P99 上升)
- 甚至出现 write stall / throttle(写被限速或阻塞)
它们经常不是 WAL 的锅,而是这条链路欠账了:
write buffer(memtable)→ flush 产生 L0 → L0 触发 compaction
1. 先把术语对齐:write buffer / memtable / flush / L0
- memtable:内存中的写入缓冲(通常是有序结构),写入先落这里。
- write buffer:可以将它理解成 memtable 占用的预算(受
write_buffer_size、memtable 数量等影响)。 - flush:把 immutable memtable 持久化为 SST 文件(通常落到 L0)。
- L0:RocksDB 分层里的第 0 层,文件之间可能重叠,读放大敏感,也容易引发后续 compaction 压力。
2. 为什么会出现 L0 堆积:这是“后台欠账”的最直接信号
当写入速度 > flush + compaction 的后台能力时,就会出现:
- memtable 频繁转 immutable
- flush 不断生成 L0 文件
- L0 文件数上升,读放大变差
- compaction 跟不上,进一步挤占 IO
- 最终触发写入限速/阻塞(保护系统)
这是一条典型的“雪球链”:越欠账,越抖;越抖,越欠账。
3. 写入为什么会 stall:两道“保险丝”
RocksDB 需要保证自己不会把系统写爆或把读拖垮,所以常见有两类保护动作:
- write throttle:还能写,但限速(更像“踩刹车”)。
- write stall:直接卡写(更像“停车”)。
触发点通常与两类资源有关:
- memtable/immutable 太多:flush 跟不上。
- L0 太多:compaction 跟不上(且 L0 对读放大影响大)。
4. 应当看哪些指标(从最有用的开始)
建议优先盯这几类“因果链中间量”:
- immutable memtable 数量:是否持续上升?
- L0 文件数量 / L0 size:是否持续上升?是否触顶?
- compaction backlog / pending bytes:后台是否欠账?
- flush/compaction 的吞吐与耗时分布:是否出现长尾?
- stall/throttle 次数:是否频繁触发保护?
经验:如果你只看“磁盘利用率/IOPS”,很容易误判。关键是 队列化 + 欠账。
5. 怎么调:不要从“参数”开始,要从“瓶颈归因”开始
先问两个问题:
- flush 慢 还是 compaction 慢?
- 主要瓶颈是 IO 还是 CPU(压缩/校验)?
5.1 flush 跟不上:在”攒 immutable”
常见策略:
- 适当增大写入缓冲预算(例如调
write_buffer_size、可用 memtable 数量)以吸收短峰值 - 提升 flush 并行/预算(取决于你的 RocksDB 版本与配置项)
- 避免让后台任务与前台争抢同一块 IO(尤其是在 compaction 也很重时)
5.2 compaction 跟不上:在”攒 L0”
常见策略:
- 给 compaction 足够资源预算(线程/带宽),否则它永远在追债
- 让 L0 的压力不要无限累积(否则读放大和写抖动都会一起恶化)
- 如果压缩很重:确认 CPU 是否已经成为瓶颈(NVMe 上经常如此)
6. 一套最小实验:10 分钟确认“是谁欠账”
- 固定 workload,记录一段时间:
- immutable 数量
- L0 文件数
- compaction backlog
- P99 写入延迟
- 只提升 flush 能力(例如增加 flush 线程/预算)观察 L0 变化
- 只提升 compaction 能力观察 L0 与 P99 的变化
会很快知道:到底是 flush 还是 compaction 主导欠账。
7. 最小排障清单(线上写入变慢/变抖)
- 先看 L0 与 backlog:是否在持续上升?
- 再看 immutable:是否在堆积?
- 确认是不是后台任务抢占:compaction/flush 是否把 IO/CPU 打满?
- 最后才谈调参:先归因再调整,不要“玄学”改一堆参数。
8. 小结
write buffer、flush、L0 本质是一条“后台债务链”。只要你能把“欠账信号”观测出来(immutable/L0/backlog),写入抖动问题通常就能从“感觉”变成“可解释、可验证”的工程问题。