RocksDB 并发 Compaction 与 Rate Limiter

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

第 3 篇 提到 RocksDB 相对 LevelDB 的核心差异之一是 多线程 flush/compaction第 11 篇 说明 compaction 跟不上时会 Stop/Delay 前台写。二者之间的旋钮是:后台 job 并发度Env 线程池 + max_background_*)与 磁盘写带宽上限RateLimiter ,默认 Mode::kWritesOnly)。

本文是系列 第 12 篇 ,源码锚点:RocksDB 9.xdb/compaction/compaction_job.hdb/db_impl/db_impl_compaction_flush.ccMaybeScheduleFlushOrCompaction)、util/rate_limiter.ccinclude/rocksdb/rate_limiter.h。令牌桶算法与 kWritesOnly 配置示例见 storage/67 写入性能优化 第六节------本篇写 内核调度挂载点与 I/O 优先级,不重复该文 MySQL/PG 对比段落。

版本锚定 :RocksDB 9.x 。实验:reproduce/run_exp12.sh exp12 (RateLimiter 开/关,3 轮中位数;见 §七 本机片段)。


一、CompactionJob 生命周期与并发模型

单个自动或手动 compaction 对应一个 CompactionJob 实例(db/compaction/compaction_job.h 类注释):

text 复制代码
Prepare()  →  Run()  →  Install()
(mutex)      (无 mutex)   (mutex)
阶段 职责 并发
Prepare 划分 subcompaction 边界(GenSubcompactionBoundaries 持 DB mutex
Run 启动子任务线程,ProcessKeyValueCompaction,校验输出 SST 释放 mutex;多 subcompaction 并行
Install LogAndApply 把新 SST 写入 Version,释放输入文件 持 DB mutex

Subcompaction :大 compaction 按 key 范围切分为多个 SubcompactionState ,各自 CompactionOutputs 写 SST;compaction_job_stats_ 聚合各子任务统计供 EventListener 使用。

并发上限受以下选项约束:

  • max_subcompactions:单 job 内并行 subcompaction 数;
  • max_background_compactions / max_background_jobs :DB 级 同时运行的 CompactionJob 数
  • CompactionPriority::kRoundRobin 等:可能 AcquireSubcompactionResources 向 Env 借额外线程(compaction_job.h 私有方法注释)。

二、Env 线程池:Flush 与 Compaction 分离

DBImpl::MaybeScheduleFlushOrCompactiondb/db_impl/db_impl_compaction_flush.cc)在 持有 mutex 下循环 env_->Schedule

flowchart LR MS[&#34;MaybeScheduleFlushOrCompaction&#34;] --> FL[&#34;HIGH pool: BGWorkFlush&#34;] MS --> FL2[&#34;LOW pool: BGWorkFlush<br/>(if HIGH empty)&#34;] MS --> CP[&#34;LOW pool: BGWorkCompaction&#34;]

要点:

  1. Flush 优先使用 Env::Priority::HIGH 后台池(max_background_flushes / max_background_jobs 推导的 max_flushes)。
  2. 若 HIGH 池线程数为 0,flush 降级 到 LOW 池(与 compaction 共享),避免无 flush 线程可用。
  3. Compaction 始终在 LOW 优先级池调度,直到 bg_compaction_scheduled_ + bg_bottom_compaction_scheduled_ 达到 GetBGJobLimits().max_compactionsunscheduled_compactions_ > 0

GetBGJobLimits(同文件):当仅设置 max_background_jobs 时,约 1/4 线程给 flush、其余给 compaction;若 write_controller_.NeedSpeedupCompaction() 为真(stall 压力),可 临时提高 compaction 并发 (与第 11 篇 CompactionPressureToken 同族)。


三、RateLimiter:kWritesOnly 与 Request 路径

DB 级 Options::rate_limiter 默认由 NewGenericRateLimiter 构造(util/rate_limiter.cc),第四个参数RateLimiter::Mode

cpp 复制代码
RateLimiter* NewGenericRateLimiter(
    int64_t rate_bytes_per_sec,
    int64_t refill_period_us /* = 100 * 1000 */,
    int32_t fairness /* = 10 */,
    RateLimiter::Mode mode /* = RateLimiter::Mode::kWritesOnly */,
    bool auto_tuned /* = false */,
    int64_t single_burst_bytes /* = 0 */);

Mode::kWritesOnlyinclude/rocksdb/rate_limiter.h):仅对 写 I/O (flush、compaction 输出、部分 WAL 路径)调用 RateLimiter::Request读 I/O 不计入 配额。另有 kReadsOnlykAllIo 等模式。

GenericRateLimiter 实现要点(util/rate_limiter.cc):

  • 令牌桶 :每 refill_period_us(默认 100ms)补充 refill_bytes_per_period_
  • 多优先级队列Env::IO_USER / IO_HIGH / IO_MID / IO_LOWGeneratePriorityIterationOrderLockedfairness 随机化 HIGH 与 MID/LOW 的相对顺序;
  • RequestToken :在 IsRateLimited(op_type) 为真时阻塞直到获得字节配额;Statistics 记 NUMBER_RATE_LIMITER_DRAINS

Compaction 写 SST 时使用 Env::IOPriorityCompactionJob::GetRateLimiterPriority):write stall 期间可能 提高 compaction I/O 优先级 ,与前台写争用同一 kWritesOnly 桶。


四、与 Write Stall、DelayWrite 的三角关系

三类机制同时作用于 磁盘写带宽

机制 作用对象 行为
RateLimiter 后台(及受控的前台)写 I/O 硬 cap bytes/s;阻塞在 Request
DelayWrite 前台 WriteImpl delayed_write_rate 主动 sleep
Stop token 前台 WriteImpl 完全等待 flush/compaction

rate_limiter_bytes_per_sec 过小

  • Compaction 变慢 → L0 / pending bytes 上升 → Delay/Stop(第 11 篇);
  • Flush 变慢 → immutable MemTable 堆积 → MemtableLimit stall。

不限速max_background_compactions 过小

  • 并发 job 不足 → 同样触发 stall,但 磁盘可能打满(P99 抖动)。

工程上常在 shared nothing 多租户Flink 与 compaction 同盘 时用 RateLimiter 保底 ;在 专用 NVMe 上更依赖 提高 max_background_jobscompaction 优先级 (详见 storage/67 6.2--6.3)。

4.1 文献与工业对照:并发 compaction 从哪来

来源 类型 本文引用点
Sears & Ramanan, bLSM (SIGMOD 2012) A 级论文 多核并行 compaction 、控制 L0 run 数以缩短 stall;RocksDB 的 CompactionJob + subcompaction 属同思路的工程演化
LevelDB BackgroundCompaction A 级源码 单线程 flush/compaction 互斥------bLSM 论文的对比基线(第 2 篇
RocksDB Wiki, Thread Pool / Rate Limiter A 级文档 Env HIGH/LOW 池与 kWritesOnly 令牌桶语义
Mark Callaghan, RocksDB Blog Write Stalls B 级 生产 trace:compaction 并发不足时 stall 延长(现象线索)

争论点 :提高 max_background_jobs 能否 线性 缩短 compaction 债务?bLSM 与后续工业实践表明受 磁盘带宽、单 key range 依赖、subcompaction 切分 限制------RocksDB 在 stall 压力下用 NeedSpeedupCompaction() 临时加并发,不是无上限扩展。

4.2 工程间隙

论文/教程 Flink / TiKV 生产
单机 SSD、均匀写 checkpoint 读 SST + compaction 写 同盘
RateLimiter 只限引擎内 I/O cgroup / 云盘 IOPS 配额 在外层 再限一层
短 benchmark fillrandom 长寿命 state + tombstone 使 compaction CPU 与 I/O 并重

五、CompactionJob::Run 与 I/O 统计

CompactionJob::Rundb/compaction/compaction_job.cc)在 subcompaction 线程中:

  1. OpenCompactionOutputFile → 经 WritableFileWriter 写块;
  2. 写路径调用 RateLimiter::RequestToken(若 DB 配置了 limiter);
  3. RecordCompactionIOStats 累加 CompactionJobStats(读/写字节、耗时)。

Statistics tick 示例:COMPACTION_READ_BYTESCOMPACTION_WRITE_BYTES;与 写放大 估算相关(用户字节 vs compaction 写字节,口径见 storage/31)。

Manual compactionCompactRange 仍走同一 CompactionJob 路径,但可能被 HasExclusiveManualCompaction 阻塞自动 picker 调度(MaybeScheduleFlushOrCompaction 内 early return)。


六、关键 DB 选项对照

选项 默认语义(9.x 常见) 内核效果
max_background_jobs 与 flush/compaction 分配联动 GetBGJobLimits
max_background_flushes 并行 FlushJob 上限 HIGH 池 schedule 循环
max_background_compactions 并行 CompactionJob 上限 LOW 池 schedule 循环
max_subcompactions 单 job 内并行度 GenSubcompactionBoundaries
rate_limiter + bytes/sec 0 = 不限 GenericRateLimiter
delayed_write_rate stall 时目标写速率 WriteController

db_bench 暴露 --rate_limiter_bytes_per_sec (映射到 NewGenericRateLimiter),便于无代码实验。


七、实验 exp12:RateLimiter 对比方法论

reproduce/run_db_bench.sh exp12 比较 无限速 vs 1 MiB/s 下 fillrandom 延迟(非吞吐结论文 ,而是 可复现口径):

bash 复制代码
cd post/db/rocksdb/reproduce
ROUNDS=3 ./run_db_bench.sh   # 默认 ROUNDS=3
# 关注 output 中 Exp 12 段

脚本逻辑(run_median 函数):

  1. 对每个配置 独立建库--use_existing_db=0,跑前 rm -rf DB 目录);
  2. 执行 fillrandom --num=100000 ,共 ROUNDS 次(默认 3);
  3. 从 stdout 用 awk '/micros\/op/ {print $NF; exit}' 提取每轮 micros/op
  4. 对三轮值 排序取中位数 ,打印 no_ratelimit median micros/opratelimit_1mib_s median micros/op

有 RateLimiter 的一轮

bash 复制代码
db_bench --benchmarks=fillrandom --num=100000 \
  --rate_limiter_bytes_per_sec=1048576 --db=<path> --use_existing_db=0

本机实测 (WSL2,i9-12900K,RocksDB 9.4.0 ,2026-07-07;reproduce/run_exp12.shROUNDS=3):

text 复制代码
  run 1: 1.808 micros/op    run 2: 1.695    run 3: 1.703
no_ratelimit median micros/op: 1.703 (n=3)
  run 1: 1.665 micros/op    run 2: 1.542    run 3: 1.625
ratelimit_1mib_s median micros/op: 1.625 (n=3)

解读 :在 100k 写、~0.18s/轮 的快盘上,1 MiB/s kWritesOnly 限流几乎不改变 fillrandom 中位数 ------前台写路径与 WAL 批量落盘远未持续触顶;number.rate_limiter.drains500k 写 长跑中亦可能仅个位数。exp12 的价值是 固定三轮中位数口径 与脚本可复现性,不是 证明「1 MiB/s 必拖慢 10×」。要观察 limiter 与 stall 联动,需 更长 --num更低 bytes/sec ,或配合 iostat 看 flush/compaction 写带宽是否贴顶。

可选扩展:固定 --threads=1 、绑定 CPU、--statistics=1 观察 NUMBER_RATE_LIMITER_DRAINS第 11 篇 stall tick。

7.1 开放问题

  1. RateLimiter vs cgroup blkio :两层限速叠加时 tail 由谁主导?无统一论文;生产常 二选一 或 limiter 设略高于 cgroup 配额。
  2. Remote compaction (RocksDB 产品方向):compaction 写 I/O 移出进程后,kWritesOnly 语义是否仍覆盖 Region 本地 WAL?版本以 release note 为准(db-frontier/14)。
  3. Subcompaction 公平性 :skew key range 下并行 subcompaction 能否让 热点层 先完成?社区 issue 讨论多,无闭式最优调度

八、与相邻篇目分工

话题 本篇 他处
Write stall 状态机 已衔接 第 11 篇
Leveled picker 不展开 第 10 篇
Column Family 独立 LSM / 共享 limiter 不展开 第 13 篇
cgroup I/O 限速 不展开 storage/67

九、小结

  • CompactionJob 通过 subcompactionRun 阶段并行;DB 级并发由 MaybeScheduleFlushOrCompactionEnv HIGH/LOW 池 调度。
  • RateLimiter::kWritesOnly 默认只限 写路径 ;与 DelayWriteStop token 形成 带宽硬顶 + 背压软顶 两层控制。
  • bLSM (SIGMOD 2012)提供 多核 compaction 减 stall 的学术锚点;RocksDB CompactionJob + 线程池是工程实现,非论文逐行复现。
  • exp12 三轮中位数 1.703 vs 1.625 micros/op (快盘短跑)------说明 limiter 效果须 拉长负载或降低配额 才可见;脚本见 reproduce/run_exp12.sh

上一篇Universal、FIFO 与 Write Stall

下一篇Column Family

返回 系列目录


参考资料

  1. RocksDB 9.x 源码:db/compaction/compaction_job.hdb/compaction/compaction_job.ccdb/db_impl/db_impl_compaction_flush.ccMaybeScheduleFlushOrCompactionGetBGJobLimits);util/rate_limiter.ccinclude/rocksdb/rate_limiter.h(A 级)。
  2. RocksDB Wiki, Thread Pool / Rate Limiter / Compaction(A 级)。
  3. Sears & Ramanan, bLSM: A General Purpose Log Structured Merge Tree, SIGMOD 2012(多核 compaction;A 级)。
  4. storage/67 写入性能优化(RateLimiter 配置、kWritesOnly、fairness;本篇引用不重复全文)。
  5. storage/32 RocksDB 实践max_background_jobs 等生产模板)。
  6. 本机 exp12:reproduce/output/exp12_ratelimit.txt(median 1.703 vs 1.625 micros/op,2026-07-07)。
  7. 第 3、11 篇 架构 diff 与 write stall 背景。
相关推荐
闻道且行之3 小时前
图片处理助手|泊松融合原理 + C++ 工程实现,seamlessClone 三模式一次讲透
数据库·c++·人工智能·opencv
夏炳辉.4 小时前
PostgreSQL 高可用集群核心配置参数全解:从原生流复制到 Patroni 企业级方案
数据库·postgresql
努力的小雨4 小时前
KES 开启 SSL 前,证书、端口和客户端要一起验
数据库
DevOps老兵5 小时前
AI全栈知识07:向量数据库 - Milvus/Chroma实战
数据库·ai·milvus
这个DBA有点耶5 小时前
当数据库从“存储”走向“决策”:金仓数据库的融合架构之路
数据库·架构·aigc
smilejingwei5 小时前
Trae+SQLazy 实践 SQL 国产化移植:Oracle => 达梦
数据库·sql·oracle
意疏6 小时前
2026年远控软件安全横评:六款主流工具逐项核查——官方文档、一手实测与安全事件,全摊开
大数据·前端·数据库
名不经传的养虾人6 小时前
从0到1:企业级AI项目迭代日记 Vol.90|Agent变快了,Judge定下来了
大数据·数据库·人工智能·ai编程·企业ai
冰之杍6 小时前
MySQL utf8mb3 → utf8mb4 完整修改方案
数据库·mysql