原文发布于 quant67.com,转载请保留出处。
第 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 级)。
一、排查框架:先分层再下钻
| 层级 | 先问什么 | 首选信号 |
|---|---|---|
| 文件系统 | 块/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 ≫ 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(仍接受写入,但限速):
text
[default] Stalling writes because we have %d level-0 files rate %lu
Stop(前台写阻塞):
text
[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):
text
[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 字节 |
命中率 ≈Δhit+ΔmissΔhit(采样窗口内需取增量,非累计绝对值)。
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 另有:
text
[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 行为(机制验证,非保证触发):
bash
cd post/db/rocksdb/reproduce
./build_rocksdb.sh
export DB_BENCH="${PWD}/rocksdb/db_bench"
./run_db_bench.sh # 含 exp11;或单独跑 exp11 段
exp11 核心参数:
bash
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,经删减):
text
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 侧调优)。