RocksDB 经典故障排查:L0、compaction 与 write stall

原文发布于 quant67.com,转载请保留出处。

第 10--12 篇 讲过 leveled 不变式、StopWritesTriggerWriteController 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.h DB::Properties 为准)。

环境说明 :WSL2 Linux 6.6.x,RocksDB 9.4.0reproduce/build_rocksdb.sh);复现见 reproduce/run_db_bench.sh exp11run_properties.sh。§七 引用本机 exp11 实测片段(与 第 11 篇 一致);LOG 行格式来自源码字符串(A 级)。


一、排查框架:先分层再下钻

flowchart TD SYM[&#34;Symptom: slow write / read / hang&#34;] SYM --> Q1{&#34;rocksdb.is-write-stopped = true?&#34;} Q1 -->|yes| STALL[&#34;Write stall chain<br/>see LOG + cf-write-stall-stats&#34;] Q1 -->|no| Q2{&#34;L0 files high?&#34;} Q2 -->|yes| L0[&#34;L0 pile-up<br/>compaction backlog&#34;] Q2 -->|no| Q3{&#34;block cache hit ratio low?&#34;} Q3 -->|yes| CACHE[&#34;Block cache pressure&#34;] Q3 -->|no| Q4{&#34;df shows full / ENOSPC?&#34;} Q4 -->|yes| DISK[&#34;Disk full behavior<br/>storage/76&#34;] Q4 -->|no| OTHER[&#34;Snapshots / hot CF / ingest&#34;]
层级 先问什么 首选信号
文件系统 块/inode/元数据是否耗尽? df -hdf -i;Btrfs btrfs filesystem usage
LSM 结构 L0 是否失控?pending bytes 是否爆表? rocksdb.num-files-at-level0rocksdb.estimate-pending-compaction-bytes
缓存 读路径是否反复落盘? rocksdb.block.cache.hit/miss(Statistics)
控制 是否处于 stop / delay? rocksdb.is-write-stoppedrocksdb.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 StatisticsDB::Properties

2.1 LSM 结构与 compaction

Property 含义 典型异常
rocksdb.num-files-at-level{N} NN 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++ / JNIdb->GetProperty("rocksdb.stats", &str)
  • ldb (编译 RocksDB 后):ldb --db=/path/to/db dump_live_files 与 property 子命令。
  • db_bench--benchmarks=fillrandom,stats 在 benchmark 结束打印 rocksdb.stats 片段。
  • Flinkstate.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::RecalculateWriteStallConditionsdb/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 产出

增量 checkpoint 前 同步 Flush短期推升 L0 文件数第 16 篇)。若 checkpoint 间隔内 L0 已常年偏高,syncDuration 与 stall 叠加------stream/13 建议同时看 state 膨胀rocksdb.num-files-at-level0

3.4 处置方向(机制层,非参数大全)

  1. 确认 compaction 是否在跑num-running-compactionsLOGCompaction start / Compaction finished
  2. 区分写太快 vs compaction 太慢 :写速率、RateLimiter第 12 篇)、磁盘带宽。
  3. 临时缓解CompactRange、调低 level0_slowdown_writes_trigger提前 stall 而非根治------长期需增 max_background_jobs 或降写入、改 Universal(写放大权衡见 storage/31)。
  4. 嵌入场景:Flink 单 subtask 过热时,先查 KeyGroup 倾斜(stream/13),再查该 subtask 本地 LOG。

四、Compaction 跟不上

4.1 pending bytes 链路

除 L0 文件数 外,RocksDB 还跟踪 estimated pending compaction bytes 。超过 soft_pending_compaction_bytes_limitdelay ;超过 hard_pending_compaction_bytes_limitstop

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-sizeestimate-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Δhit+Δmiss \approx \frac{\Delta hit}{\Delta hit + \Delta miss} ≈Δhit+ΔmissΔhit(采样窗口内需取增量,非累计绝对值)。

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 ErrorHandlerStatus::NoSpace() 的处理(db/error_handler.cc 及测试 error_handler_fs_test.cc,A 级):

  • 后台 flush/compaction 遇 NoSpace → 记录 background error,触发 write stall 或停止 compaction;
  • 前台写可能收到 Status::NoSpace() 或阻塞在 WriteThread stall 队列(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=2stop=4,在 高速 SSD + 1.45s 跑完可能 stall=0 ------这不否定 §1.1 状态机;只说明 短跑 exp11 不能替代生产 LOG 排查 。稳定复现 stall 需更长 --duration、更慢磁盘,或实验环境 pause compaction。

若 stall 非零 ,预期还有:LOG 出现 §3.2 的 Stalling/Stopping writesrocksdb.cf-write-stall-stats 计数上升。

run_properties.sh 在短跑后保留 output/log_tail.txt 供 LOG 格式对照(输出经删减后引用)。

7.1 开放问题

  1. 跨层归因 :Flink syncDuration 变长时,如何区分 RocksDB stall vs 远程 state 上传 vs GC?需 时间线对齐 (checkpoint 事件 + TM LOG + GetProperty 采样);无单一 metrics 公式
  2. 可观测性缺口Statistics tick 是累计量,生产 tail 需 增量采样PerfContext;OpenTelemetry 导出仍在演进,版本以 release note 为准。
  3. 自动根因 :能否从 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-level0pending-compaction-bytes
Kafka Streams restore 慢 changelog lag、本地 store 目录 同单机 LSM;另查 consumer
MyRocks / 自建 LOGrocksdb.stats 全文同上

九、小结

  • L0 堆积 :看 num-files-at-level0 + LOG level-0 files;与 leveled 不变式、flush 速率直接相关。
  • Compaction 落后 :看 estimate-pending-compaction-bytesnum-running-compactions + LOG pending 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 湖仓,并给出全栈阅读路径。


上一篇生产嵌入对照

下一篇选型与存储栈阅读地图

返回 系列目录


参考资料

  1. RocksDB Wiki, Troubleshooting / Write Stalls / Background Error Handling / Statistics(A 级)。
  2. RocksDB 9.x 源码:db/internal_stats.cc(property 名);db/column_family.ccRecalculateWriteStallConditions LOG);db/write_thread.cc(stall 队列);db/error_handler.cc(NoSpace;A 级)。
  3. Sears & Ramanan, bLSM, SIGMOD 2012(stall / 多核 compaction 学术锚点;A 级)。
  4. storage/76 磁盘空间耗尽(ENOSPC 链、RocksDB stall 默认行为)。
  5. storage/31 LSM 调参storage/32 RocksDB 实践(调参边界)。
  6. 本系列 第 10--12 篇(compaction / stall);第 11 篇 exp11第 16 篇(Flink 交叉)。
  7. 本机 exp11:reproduce/output/exp11_stall.txt(stall.micros=0,2026-07-07)。
  8. stream-processing 第 13 篇(Flink 侧调优)。
相关推荐
xfhuangfu2 小时前
Oracle中建立到CDB和PDB的连接
数据库·oracle·rpc
小小龙学IT2 小时前
DuckDB 深度实战:用 C++ 在进程内跑一个「分析型数据库」
数据库·c++
NineData2 小时前
DTCC 2026 预告|NineData CEO& 创始人叶正盛:面向 AI Agent 的数据库 DevOps 与数据复制实践
数据库·人工智能·数据库开发·devops·ninedata·数据库技术·dtcc
J_bean3 小时前
MySQL 事务是否必须手动开启?
数据库·mysql·数据库事务·自动提交·手动提交
J_bean3 小时前
MySQL InnoDB 如何检测死锁、判定死锁、处理死锁
数据库·mysql·死锁·死锁检测·处理死锁·死锁判定
浪子明X4 小时前
从 MongoDB 文档到关系模型:构建可重跑、可对账的数据迁移流水线
数据库·mongodb·oracle
杜子不疼.4 小时前
国产数据库撑起固井软件自主化:金仓 × 中海油服「海恒 Cemsol」落地解析
数据库
隔窗听雨眠4 小时前
GBase 8s并发控制深度解析:封锁机制、隔离级别与死锁处理全攻略
服务器·数据库·oracle
刀客Doc4 小时前
即时零售不只是多一个渠道,品牌开始重做终端生意
大数据·数据库