第
10–12 篇 讲过 leveled
不变式、StopWritesTrigger 与
WriteController stall 状态机。第
16 篇 把 Flink 增量 checkpoint 与 SST
文件语义对齐。生产里运维同学更常遇到的是:延迟突然放大、磁盘占用爬升、checkpoint
sync 变长、进程「像 hang
住」却不报错——四类症状往往对应 L0 堆积、compaction
跟不上、Block Cache 失效、磁盘 ENOSPC 触发的 write
stall。
本文是系列 第 17 篇,给出
可核对的信号源:DB::GetProperty
键名(RocksDB 9.x db/internal_stats.cc,A
级)、LOG 中 ROCKS_LOG_WARN
行格式(db/column_family.cc,A
级)、Statistics tick 与 storage/76
磁盘耗尽 的应用层表现。参数 cookbook 仍见 storage/32;Flink
作业侧见 stream/13
状态调优。
版本锚定:RocksDB 9.x(property 名以
include/rocksdb/db.hDB::Properties为准)。
环境说明:WSL2 Linux 6.6.x,RocksDB 9.4.0(reproduce/build_rocksdb.sh);复现见reproduce/run_db_bench.shexp11、run_properties.sh。§七 引用本机 exp11 实测片段(与 第 11 篇 一致);LOG 行格式来自源码字符串(A 级)。
一、排查框架:先分层再下钻
flowchart TD
SYM["Symptom: slow write / read / hang"]
SYM --> Q1{"rocksdb.is-write-stopped = true?"}
Q1 -->|yes| STALL["Write stall chain<br/>see LOG + cf-write-stall-stats"]
Q1 -->|no| Q2{"L0 files high?"}
Q2 -->|yes| L0["L0 pile-up<br/>compaction backlog"]
Q2 -->|no| Q3{"block cache hit ratio low?"}
Q3 -->|yes| CACHE["Block cache pressure"]
Q3 -->|no| Q4{"df shows full / ENOSPC?"}
Q4 -->|yes| DISK["Disk full behavior<br/>storage/76"]
Q4 -->|no| OTHER["Snapshots / hot CF / ingest"]
| 层级 | 先问什么 | 首选信号 |
|---|---|---|
| 文件系统 | 块/inode/元数据是否耗尽? | df -h、df -i;Btrfs
btrfs filesystem usage |
| LSM 结构 | L0 是否失控?pending bytes 是否爆表? | rocksdb.num-files-at-level0、rocksdb.estimate-pending-compaction-bytes |
| 缓存 | 读路径是否反复落盘? | rocksdb.block.cache.hit/miss(Statistics) |
| 控制 | 是否处于 stop / delay? | rocksdb.is-write-stopped、rocksdb.actual-delayed-write-rate |
| 嵌入场景 | 是单 CF 还是 Flink 多 CF 共 slot? | TM 上 per-subtask LOG;Flink native metrics |
1.1 Stall 是背压,不是「神秘 hang」
Write stall 在 Wiki(A 级)与 bLSM(SIGMOD
2012)语境下是 故意降速或停写,给
flush/compaction 时间消化 debt——排查时先确认
是否处于设计内的 backpressure,再调参。与
ENOSPC、JVM GC、Flink barrier 对齐等 外层
stall 叠加时,仅看 RocksDB property 不够;但
rocksdb.is-write-stopped 与
LOG Stalling/Stopping writes 仍是
内核层第一手信号(机制详见 第 11
篇)。
二、GetProperty:常用键名
RocksDB 在 db/internal_stats.cc 注册
property,公开名带 rocksdb.
前缀(A 级源码)。下表按故障类型分组;完整列表见 Wiki
Statistics 与 DB::Properties。
2.1 LSM 结构与 compaction
| Property | 含义 | 典型异常 |
|---|---|---|
rocksdb.num-files-at-level{N} |
第 \(N\) 层 SST 文件数 | L0 \(\gg\)
level0_slowdown_writes_trigger(默认 20) |
rocksdb.stats |
文本摘要(层深、文件数、score) | 快速肉眼扫描;等同
db_bench --benchmarks=stats |
rocksdb.levelstats |
各层详细统计 | 对比 L0 vs L1+ 字节分布 |
rocksdb.estimate-pending-compaction-bytes |
待 compaction 字节估计 | 接近 soft_pending_compaction_bytes_limit →
delay |
rocksdb.compaction-pending |
是否有 compaction 待调度 | 长期 1 且 L0 涨 → 线程/IO 不足 |
rocksdb.num-running-compactions |
正在运行的 compaction 数 | 长期 < max_background_jobs 且 pending 高
→ 磁盘或 CPU 瓶颈 |
rocksdb.num-running-flushes |
正在运行的 flush 数 | 与 immutable memtable 堆积对照 |
2.2 MemTable 与 write stall
| Property | 含义 | 典型异常 |
|---|---|---|
rocksdb.num-immutable-mem-table |
未 flush 的 immutable memtable 数 | 接近 max_write_buffer_number → flush
跟不上 |
rocksdb.mem-table-flush-pending |
是否有 flush 等待 | checkpoint 前强制 flush 时常为 1 |
rocksdb.is-write-stopped |
是否 完全停写 | true → 见 LOG
Stopping writes |
rocksdb.actual-delayed-write-rate |
当前 delay 模式限速(bytes/s) | 非 0 → Stalling writes |
rocksdb.cf-write-stall-stats |
CF 级 stall 计数摘要 | 区分 L0 / memtable / pending bytes 原因 |
rocksdb.db-write-stall-stats |
DB 级 stall 计数 | 多 CF 汇总 |
2.3 空间与 Block Cache
| Property | 含义 | 典型异常 |
|---|---|---|
rocksdb.live-sst-files-size |
live SST 总字节 | 远大于逻辑 key 体积 → 写放大 / tombstone |
rocksdb.total-sst-files-size |
含 obsolete 的 SST 体积 | 与 live 差过大 → compaction 未及时回收 |
rocksdb.block-cache-capacity |
Block Cache 容量 | 与 block-cache-usage 对照 |
rocksdb.block-cache-usage |
已用 cache | 顶满 + miss 高 → 读放大 |
rocksdb.block-cache-pinned-usage |
pin 住的块 | index/filter pin 过高挤占 data |
rocksdb.estimate-table-readers-mem |
Table Cache 等估计 | 打开 SST 过多 |
2.4 读取方式
- C++ /
JNI:
db->GetProperty("rocksdb.stats", &str)。 - ldb(编译 RocksDB
后):
ldb --db=/path/to/db dump_live_files与 property 子命令。 - db_bench:
--benchmarks=fillrandom,stats在 benchmark 结束打印rocksdb.stats片段。 - Flink:
state.backend.rocksdb.metrics.enable=true后选择性暴露 native metrics(名称与 property 对应,见 Flink 文档 Metrics)。
三、L0 堆积
3.1 机制回顾
Leveled compaction 要求 L1+ 层 key
范围不重叠;L0 允许多文件 key
范围重叠(第
10 篇)。写入过快或 flush 过多时,L0 文件数上升 → 点查需
probe 多个 L0 SST → 读放大上升;超过
level0_slowdown_writes_trigger 进入
delay,超过
level0_stop_writes_trigger 进入
stop(第 11
篇)。
3.2 LOG 判读
ColumnFamilyData::RecalculateWriteStallConditions
在 db/column_family.cc 打印 WARN(A 级,以下为
源码格式,%d / %s
为运行时填充):
Delay(仍接受写入,但限速):
[default] Stalling writes because we have %d level-0 files rate %lu
Stop(前台写阻塞):
[default] Stopping writes because we have %d level-0 files
CF 名 [default] 在 Flink 中为具体 state
名(如 [count])。若同一 LOG 段
反复出现 且 %d 接近
level0_stop_writes_trigger,说明 L0 compaction
跟不上 flush 产出。
3.3 与 Flink checkpoint 的交叉
增量 checkpoint 前 同步 Flush 会
短期推升 L0 文件数(第
16 篇)。若 checkpoint 间隔内 L0
已常年偏高,syncDuration 与 stall
叠加——stream/13 建议同时看 state 膨胀 与
rocksdb.num-files-at-level0。
3.4 处置方向(机制层,非参数大全)
- 确认 compaction
是否在跑:
num-running-compactions、LOG中Compaction start/Compaction finished。 - 区分写太快 vs compaction
太慢:写速率、
RateLimiter(第 12 篇)、磁盘带宽。 - 临时缓解:
CompactRange、调低level0_slowdown_writes_trigger仅 提前 stall 而非根治——长期需增max_background_jobs或降写入、改 Universal(写放大权衡见 storage/31)。 - 嵌入场景:Flink 单 subtask 过热时,先查 KeyGroup 倾斜(stream/13),再查该 subtask 本地 LOG。
四、Compaction 跟不上
4.1 pending bytes 链路
除 L0 文件数 外,RocksDB 还跟踪
estimated pending compaction bytes。超过
soft_pending_compaction_bytes_limit →
delay;超过
hard_pending_compaction_bytes_limit →
stop。
LOG 格式(源码 column_family.cc):
[default] Stalling writes because of estimated pending compaction bytes %lu rate %lu
[default] Stopping writes because of estimated pending compaction bytes %lu
对应
property:rocksdb.estimate-pending-compaction-bytes。
4.2 常见根因
| 根因 | 信号 | 说明 |
|---|---|---|
| 磁盘带宽饱和 | iostat 高 util;compaction 耗时长 |
与 Flink checkpoint 上传 叠加(stream/13) |
max_background_jobs 过小 |
num-running-compactions 长期打满 |
第 12 篇 |
| 大 value / 宽列 | live-sst-files-size 涨速异常 |
考虑 BlobDB 或 value 拆分(超出本篇) |
disable_auto_compactions=true |
compaction-pending 长期为 1 且无进展 |
运维误配或 ingest 窗口 |
| 快照 / checkpoint 引用 | num-snapshots 高;旧 SST 不删 |
空间涨但 compaction 「无效」 |
4.3 Compaction 与读放大
Compaction 落后时 tombstone 与多版本 entry
滞留,读路径 需扫描更多 entry(第 8 篇
Iterator)。表现为 Get/Iterator 延迟升而 L0
文件数未必唯一指标——需同时看
live-sst-files-size 与
estimate-num-keys 的比值。
五、Block Cache 未命中
5.1 读路径回顾
点查经 Table Cache → Filter → Index → Data Block(第 9 篇)。Block Cache miss 每次触发磁盘读;L0 文件多时,即使 Bloom 过滤,Index/Data 块 miss 仍显著。
5.2 Statistics tick
启用
Options.statistics = CreateDBStatistics()
后(db_bench --statistics=1),常用
tick(include/rocksdb/statistics.h,A
级):
| Tick | 含义 |
|---|---|
rocksdb.block.cache.hit |
Block Cache 命中 |
rocksdb.block.cache.miss |
Block Cache 未命中 |
rocksdb.block.cache.add |
新块入 cache |
rocksdb.block.cache.bytes.insert |
入 cache 字节 |
命中率 \(\approx \frac{\Delta hit}{\Delta hit + \Delta miss}\)(采样窗口内需取增量,非累计绝对值)。
5.3 与 Flink managed memory
Flink 默认
state.backend.rocksdb.memory.managed=true,在
slot 级划分 Write Buffer vs Block
Cache(stream/12 第 4.2 节)。写缓冲占满
50% 时,读热点 scan 易出现 cache 抖动——stream/13
建议大状态 避免 full MapState.entries()
scan,与 cache 容量无关的算法层优化。
5.4 误判
- Table Cache miss(重复 open SST)与
Block Cache miss 不同;前者看
rocksdb.no.file.opens等 tick。 - Partitioned Index 下 index/filter
pin(
cache_index_and_filter_blocks)占用高优先级池,表现为 data block miss 升高——见 第 9 篇 与 storage/68。
六、ENOSPC 与 write stall
6.1 文件系统层
磁盘从 70% 填充到 ENOSPC 的行为因 ext4/XFS/Btrfs 而异(storage/76)。对 RocksDB 嵌入场景,关键结论是:
- 块耗尽 时 flush / compaction 无法写出新 SST;
- Btrfs 元数据 ENOSPC 可能
df -h仍有空间——与 RocksDB 「空间够但写失败」混淆; - PostgreSQL 遇 WAL ENOSPC 会 PANIC;RocksDB 默认 stall 而非立即报错(storage/76 第 8.2 节)。
6.2 RocksDB 应用层行为
RocksDB ErrorHandler 对
Status::NoSpace()
的处理(db/error_handler.cc 及测试
error_handler_fs_test.cc,A 级):
- 后台 flush/compaction 遇 NoSpace → 记录 background error,触发 write stall 或停止 compaction;
- 前台写可能收到
Status::NoSpace()或阻塞在WriteThreadstall 队列(Status::Incomplete("Write stall"),db/write_thread.cc)。
Wiki Background Error Handling(A
级)说明:no_space 类错误在默认配置下常导致
无限期 stall——监控上表现为 p99
延迟无穷大、CPU 不高、无明确 Java 异常(Flink TM
典型「假死」)。
DB::GetProperty("rocksdb.background-errors")
非零时应 立即查磁盘 与
rocksdb.is-write-stopped。
6.3 与 storage/76 的对照
| 现象 | storage/76 视角 | RocksDB 视角 |
|---|---|---|
df 100% |
ENOSPC、分配器退化 | flush/compaction 失败 → stall |
df 70% 但写失败 |
Btrfs metadata / inode | 同上 + 查 df -i |
| 应用 hang | 「Kafka/RocksDB stall」 | LOG stall + is-write-stopped |
| 恢复 | 删文件、扩卷、Btrfs balance | 释放空间后 stall 自动解除(默认) |
应急:先腾磁盘(删 obsolete SST 需 compaction 完成或从 checkpoint 链 pruning;Flink 远程 shared state 亦占空间),再重启仅作最后手段。
6.4 MemTable 触发的 stall(非 ENOSPC)
immutable memtable 过多时 LOG 另有:
[default] Stalling writes because we have %d immutable memtables (waiting for flush), max_write_buffer_number is set to %d rate %lu
[default] Stopping writes because we have %d immutable memtables (waiting for flush), max_write_buffer_number is set to %d
这与 ENOSPC 症状相似(写慢),但
df 仍有空间——用
num-immutable-mem-table 区分。
七、db_bench 复现(exp11)
脚本 reproduce/run_db_bench.sh
exp11 压低 L0 阈值以 试图
观察 stall 行为(机制验证,非保证触发):
cd post/db/rocksdb/reproduce
./build_rocksdb.sh
export DB_BENCH="${PWD}/rocksdb/db_bench"
./run_db_bench.sh # 含 exp11;或单独跑 exp11 段exp11 核心参数:
db_bench --benchmarks=fillrandom --num=500000 --value_size=256 \
--level0_slowdown_writes_trigger=2 \
--level0_stop_writes_trigger=4 \
--db="${OUT_DIR}/bench_dbs/exp11_stall" --statistics=1本机实测(WSL2,i9-12900K,RocksDB
9.4.0,2026-07-07;reproduce/output/exp11_stall.txt,经删减):
fillrandom : 2.901 micros/op 344660 ops/sec 1.451 seconds 500000 operations
rocksdb.stall.micros COUNT : 0
Write Stall (count): total-delays: 0, total-stops: 0
解读(与第 11 篇 §七 一致):尽管
slowdown=2、stop=4,在
高速 SSD + 1.45s 跑完 时 可能
stall=0——这不否定 §1.1 状态机;只说明 短跑
exp11 不能替代生产 LOG 排查。稳定复现 stall 需更长
--duration、更慢磁盘,或实验环境 pause
compaction。
若 stall 非零,预期还有:LOG 出现 §3.2
的
Stalling/Stopping writes;rocksdb.cf-write-stall-stats
计数上升。
run_properties.sh 在短跑后保留
output/log_tail.txt 供 LOG
格式对照(输出经删减后引用)。
7.1 开放问题
- 跨层归因:Flink
syncDuration变长时,如何区分 RocksDB stall vs 远程 state 上传 vs GC?需 时间线对齐(checkpoint 事件 + TM LOG +GetProperty采样);无单一 metrics 公式。 - 可观测性缺口:
Statisticstick 是累计量,生产 tail 需 增量采样 或PerfContext;OpenTelemetry 导出仍在演进,版本以 release note 为准。 - 自动根因:能否从 LOG + property
训练推荐
max_background_jobs/ compaction style?工业 AutoTune 实验与论文侧 workload-aware compaction 并存,默认仍以人工 POC 为主。
八、嵌入场景速查
| 场景 | 优先文件/命令 | 内核信号 |
|---|---|---|
| Flink checkpoint 慢 | TM rocksdb-localdir、Flink REST
syncDuration |
mem-table-flush-pending、L0 stall LOG |
| TiKV 读抖 | TiKV metrics + 本地 RocksDB LOG | num-files-at-level0、pending-compaction-bytes |
| Kafka Streams restore 慢 | changelog lag、本地 store 目录 | 同单机 LSM;另查 consumer |
| MyRocks / 自建 | LOG、rocksdb.stats |
全文同上 |
九、小结
- L0 堆积:看
num-files-at-level0+ LOGlevel-0 files;与 leveled 不变式、flush 速率直接相关。 - Compaction 落后:看
estimate-pending-compaction-bytes、num-running-compactions+ LOGpending compaction bytes。 - Block Cache 未命中:看 Statistics
block.cache.hit/miss;Flink 下与 managed memory 比例、scan 模式相关。 - ENOSPC:先读 storage/76
文件系统链,再看 RocksDB stall 非 crash
的默认行为与
background-errors。 - exp11 在快盘短跑 可能 stall=0;生产排查仍以 LOG + GetProperty 为准,勿以 benchmark 否定 stall 机制。
- 排查顺序:df/inode → GetProperty → LOG 源码格式对照 → Statistics;调参回 storage/32,Flink 设计回 stream/13。
下一篇 选型与存储栈阅读地图 收束 RocksDB vs InnoDB vs 列存 vs 湖仓,并给出全栈阅读路径。
上一篇:生产嵌入对照
下一篇:选型与存储栈阅读地图
返回 系列目录
参考资料
- RocksDB Wiki, Troubleshooting / Write Stalls / Background Error Handling / Statistics(A 级)。
- RocksDB 9.x
源码:
db/internal_stats.cc(property 名);db/column_family.cc(RecalculateWriteStallConditionsLOG);db/write_thread.cc(stall 队列);db/error_handler.cc(NoSpace;A 级)。 - Sears & Ramanan, bLSM, SIGMOD 2012(stall / 多核 compaction 学术锚点;A 级)。
- storage/76 磁盘空间耗尽(ENOSPC 链、RocksDB stall 默认行为)。
- storage/31 LSM 调参、storage/32 RocksDB 实践(调参边界)。
- 本系列 第 10–12 篇(compaction / stall);第 11 篇 exp11;第 16 篇(Flink 交叉)。
- 本机
exp11:
reproduce/output/exp11_stall.txt(stall.micros=0,2026-07-07)。 - stream-processing 第 13 篇(Flink 侧调优)。
同主题继续阅读
把当前热点继续串成多页阅读,而不是停在单篇消费。
【RocksDB 内核机制】Block Cache 与 Bloom:读放大裁剪
沿 TableCache 与 BlockBasedTable 读路径说明 Block Cache、Table Cache、Full/Ribbon Filter 与 Partitioned Index 如何裁剪点查 I/O;读放大度量与调参边界引用 storage/68,不重复全文推导。
【RocksDB 内核机制】Universal、FIFO 与 Write Stall
对照 Universal/FIFO/TTL compaction 策略,从 GetWriteStallConditionAndCause 与 WriteController 的 Stop/Delay token,到 DBImpl::DelayWrite 状态机、LOG 与 GetProperty 判读;exp11 用极低 L0 阈值触发 stall。
【RocksDB 内核机制】LevelDB · WAL · Compaction · Column Family · 生产嵌入
补全存储引擎三角最后一角:从 LevelDB 基线与 RocksDB 架构演进,到 WAL/MemTable/SST 写路径、Get/Iterator 读路径、Leveled/Universal compaction 与 write stall,再到 Column Family、事务、Checkpoint 与 Flink/TiKV 嵌入对照。
【RocksDB 内核机制】LSM 生态全景:存储引擎三角与 RocksDB 生态位
闭合存储引擎三角的最后一角:对照 PG B-Tree 与列存 scan,定位 LSM 写优化引擎在 TiKV、Flink、Kafka Streams、ClickHouse Embedded 中的嵌入方式,并划分本系列与 lsm-tree DIY、storage/31–32、stream/12 的分工边界。