土法炼钢兴趣小组的算法知识备份

【RocksDB 内核机制】生产嵌入对照:Flink · TiKV · Kafka Streams

文章导航

分类入口
databasestorage
标签入口
#rocksdb#flink#tikv#kafka-streams#keygroup#incremental-checkpoint#column-family#embedded-production#lsm-tree

目录

stream-processing 第 12 篇 已从 Flink 作业侧 说明:EmbeddedRocksDBStateBackend 为每个 subtask 打开独立 RocksDB、每种 registered state 对应一个 Column Family(CF)、KeyGroup 前缀决定 key 排序区间,增量 checkpoint 上传自上次 completed checkpoint 以来 新增的 SST。第 13 篇从 state 设计与调参 讨论写放大、hot key 与 checkpoint 争抢磁盘。

本文是系列 第 16 篇,站在 RocksDB 内核 回答三个互补问题——不迁移 stream/12 的 barrier 对齐与 Web UI 解读全文:

  1. Flink 的 KeyGroup | Namespace | UserKey 在 RocksDB InternalKey 排序 里意味着什么?为何 Rescale 必须保留 KeyGroup 前缀?
  2. 增量 checkpoint 依赖 RocksDB 哪些 不可变文件语义(SST、MANIFEST、Checkpoint API)?Compaction 如何改变 Flink 的 shared state 引用链?
  3. TiKV、Kafka Streams 与 Flink 在 单 DB 实例边界、CF 划分、容错日志 上有何差异?

本文是「RocksDB 内核机制」系列第 16 篇(共 18 篇)。→ 系列目录

篇目 核心内容
第 15 篇 · Checkpoint / Ingest 硬链接快照、SstFileWriter
第 16 篇 · 生产嵌入对照 KeyGroup 内核侧、增量 SST、三系统对照表
第 17 篇 · 经典故障与排查 L0 堆积、LOG / GetProperty

版本锚定:RocksDB 9.x;Flink 1.20+ / 2.xEmbeddedRocksDBStateBackend);TiKV 7.x+ Region 本地引擎(文档 Storage);Kafka Streams 3.x+RocksDBStore)。
环境说明:本机 WSL2(内核 6.6.x),未部署 Flink 集群。机制来自 Flink / TiKV 官方文档、flink-statebackend-rocksdb 源码与 RocksDB Wiki;不粘贴未执行的 Web UI 截图,也不伪造 checkpoint 耗时或 checkpointedSize 数字。文末给出 Docker 复现步骤,供读者在自有环境验证。


一、与 stream/12、stream/13 的分工

视角 stream/12–13 本系列第 16 篇
读者问题 作业如何配 state backend、checkpoint 传什么、Web UI 怎么看 SST 如何产生、为何 Flush 阻塞 sync 阶段、Compaction 如何改文件集
KeyGroup CompositeKeySerializationUtils 前缀规则 前缀如何进入 CF 内有序 key、与 prefix_extractor / 范围 scan 的关系
增量 checkpoint RocksIncrementalSnapshotStrategy 上传 diff Checkpoint::CreateCheckpoint 硬链接、MANIFEST 版本、与 第 15 篇 接口对齐
调参 RocksDBOptionsFactory、managed memory、hot key 设计 write_buffer_sizemax_background_jobs内核 stall 上的落点(详见 第 11–12 篇

stream/13 强调 改 state 设计优先于拧参数;本篇说明:即便参数正确,L0 堆积或 pending compaction bytes 触发的 write stall 仍会在 checkpoint 前的 强制 Flush 阶段暴露为 syncDuration 上升——这是 LSM 层信号,不是 Flink 调度器单独能消掉的。


二、Flink:KeyGroup 前缀的内核含义

2.1 CF 内 key 布局与排序

Flink 在 CompositeKeySerializationUtils 中构造 RocksDB key(Apache Flink 源码,A 级):

\[ \text{key}_{\text{rocksdb}} = \underbrace{\text{KeyGroupPrefix}}_{1\text{ 或 }2\text{ 字节}} \,\|\, \text{Namespace} \,\|\, \text{UserKey} \]

RocksDB 在 CF 内按 完整 byte[] key 字典序排序(第 13 篇 Column Family)。因此:

flowchart LR
  subgraph CF["Column Family: count"]
    K0["KeyGroup=3 | NS | userA"]
    K1["KeyGroup=3 | NS | userB"]
    K2["KeyGroup=4 | NS | userC"]
  end
  K0 --> K1 --> K2

2.2 与读路径、Block Cache 的交叉

点查 ValueState.value()第 7–9 篇 路径:MemTable → L0(可能多文件)→ L1…Ln。KeyGroup 前缀 改变 LSM 层级查找算法,但影响:

stream/13 的 hot key 诊断应同时看 Flink subtask 指标 与 RocksDB rocksdb.num-files-at-level0(第 17 篇)。

2.3 多 CF 与共享 WAL

每种 registered state 一个 CF(Flink 文档 State Backends,A 级)。内核侧后果(第 13 篇):


三、增量 checkpoint:SST 上传的内核链

3.1 为何必须先 Flush

增量路径在 barrier 对齐后的 snapshot 阶段 同步调用 Flush(Flink 官方博客 Managing Large State in Apache Flink: An Intro to Incremental Checkpointing,B 级;与 RocksIncrementalSnapshotStrategy 源码一致,A 级)。内核原因:

  1. MemTable 中的数据 尚未成为不可变 SST,无法作为稳定文件清单 diff。
  2. Flush 产出 L0 SST 后,MANIFEST 记录新 file_number;此后该文件 内容不变,直到 Compaction 淘汰。

这与 第 5 篇 MemTable FlushFlushJob 路径一致:Immutable MemTable → 顺序写 SST → LogAndApply 安装新 Version。

3.2 Checkpoint API 与硬链接

Flink 调用 RocksDB Checkpointutilities/checkpoint/checkpoint_impl.*,A 级)在本地临时目录创建 时间点一致 视图(第 15 篇):

Flink 对比 上次 completed checkpoint 的文件清单:

sequenceDiagram
  participant FL as Flink Snapshot
  participant RB as RocksDB DBImpl
  participant CP as Checkpoint dir
  participant FS as Remote CheckpointStorage
  FL->>RB: Flush()
  RB->>RB: L0 SST + VersionEdit
  FL->>RB: CreateCheckpoint(CP)
  RB->>CP: hardlink SST, copy MANIFEST
  FL->>FL: diff vs last completed CP
  FL->>FS: upload new SST only
  FL->>FS: add ref to shared SST

3.3 Compaction 与 shared state 引用

Compaction 合并 SST 后,旧 file_number 从当前 Version 移除,但 仍被未完成 checkpoint 引用 时不能物理删除——Flink notifyCheckpointComplete 后递减引用,与 RocksDB SuperVersion 引用计数 同构不同层(stream/12 第 5.2 节)。

内核侧推论(机制推导,非 fabricated benchmark):

3.4 与全量 Savepoint 的边界

Savepoint(canonical)走 全量 路径,不依赖 SST 引用链(stream/12 第 5.1 节)。内核上等价于迭代 kv 或导出完整 checkpoint 目录再整体上传——CPU 与网络体积 更高,但 恢复不依赖历史增量链。生产上增量 checkpoint 与 Savepoint 分工见 stream/11–12;本篇只强调:增量语义完全建立在 RocksDB 不可变 SST + MANIFEST 之上


下表比较 单个 RocksDB 实例 在三种系统中的角色。TiKV Raft / Percolator 不在此展开(续作见 db-frontier/12 HTAP)。

维度 Flink EmbeddedRocksDB TiKV Region 引擎 Kafka Streams RocksDBStore
DB 实例边界 每个 subtask × 每个 keyed 算子一个 DB 每个 Region 一个(或一组 CF) 每个 task × 每个 state store 一个 DB
CF 划分 每种 registered state 一个 CF 默认/raft/data 等多 CF(模块隔离) 每个 store 通常单 CF
Key 编码 KeyGroup \| NS \| UserKey 带 MVCC 时间戳的内部 key(TiKV Storage partition \| key \| seq 类 store key
容错日志 Flink checkpoint / savepoint Raft log(与 RocksDB WAL 分层) Changelog topic 重放
快照模型 增量 SST 上传 + 引用 BR / snapshot 工具链(分布式层) Standby replica + changelog
一致性快照 API RocksDB Checkpoint + Flush 分布式 snapshot 与本地 CF 一致点 StoreChangelogReader 恢复
Compaction 运维 TM 本地盘;与 CP 争抢 I/O 节点本地盘;Region split/merge Broker + 本地盘
典型 stall 信号 syncDuration、subtask 背压 Region 读延迟、Raft apply 滞后 Consumer lag、restore 慢
本系列锚点 第 4–6、11、15–17 篇 第 13–14、18 篇 第 16 篇对照

TiKV(TiKV Documentation Storage,B 级):RocksDB 是 Region 本地有序 KV;分布式事务时间戳在 上层编码,不是 RocksDB Snapshot 的 sequence 语义。读 TiKV 性能问题需 分层:Coprocessor / Raft / 本地 LSM。

Kafka Streams(Confluent / Apache Kafka 文档 RocksDB State Store,B 级):状态变更 双写 本地 RocksDB 与 changelog topic;恢复时 replay changelog 重建 store。没有 Flink 式 增量 SST 上传,但 Compaction 与磁盘满 仍走同一套 write stall(storage/76 ENOSPC)。


五、ClickHouse Embedded 与边界

第 1 篇 LSM 生态 已说明:ClickHouse 主存储是 MergeTree(列存角),RocksDB 出现在 EmbeddedRocksDB 等 辅助 KV。选型上属于 列存为主、点查 KV 为辅——与纯 Flink/TiKV 负载不同,详见 第 18 篇

本篇不展开:ForSt 远程 SST 实验后端、Changelog state backend(FLIP-158)与 RocksDB 叠加、RocksDBOptionsFactory 全参数表(见 stream/13 与 storage/32)。


六、Docker 复现步骤(不伪造指标)

下列步骤在 读者自有环境 验证 Flink + RocksDB 目录布局与 checkpoint 定性 行为;本文未在本地执行,故不写入 checkpoint 毫秒数或 GB 数。

6.1 环境

6.2 docker-compose.yaml 示例

services:
  jobmanager:
    image: flink:1.20-scala_2.12-java11
    ports:
      - "8081:8081"
    command: jobmanager
    environment:
      - |
        FLINK_PROPERTIES=
        jobmanager.rpc.address: jobmanager
        state.backend.type: rocksdb
        execution.checkpointing.incremental: true
        execution.checkpointing.interval: 60s
        execution.checkpointing.dir: file:///checkpoint-data
        state.backend.rocksdb.localdir: /rocksdb-local
        state.backend.rocksdb.memory.managed: true
    volumes:
      - ./flink-data/checkpoints:/checkpoint-data
      - ./rocksdb-local:/rocksdb-local

  taskmanager:
    image: flink:1.20-scala_2.12-java11
    depends_on:
      - jobmanager
    command: taskmanager
    environment:
      - |
        FLINK_PROPERTIES=
        jobmanager.rpc.address: jobmanager
        taskmanager.numberOfTaskSlots: 4
        state.backend.type: rocksdb
        execution.checkpointing.incremental: true
        execution.checkpointing.dir: file:///checkpoint-data
        state.backend.rocksdb.localdir: /rocksdb-local
    volumes:
      - ./flink-data/checkpoints:/checkpoint-data
      - ./rocksdb-local:/rocksdb-local
    deploy:
      resources:
        limits:
          memory: 4g

6.3 提交带 Keyed State 的作业

使用 Flink 发行版自带的 State Machine Example 或自行打包的 keyed counter JAR(需 EmbeddedRocksDBStateBackend(true)enableCheckpointing)。提交后:

docker compose up -d
# 将作业 JAR 复制进 JobManager 容器后:
docker compose exec jobmanager flink run \
  -d /opt/flink/examples/streaming/StateMachineExample.jar

6.4 观测清单(定性)

  1. RocksDB 目录:在 TaskManager 容器内查看 /rocksdb-local{jobId}/{operatorId}/{subtaskIndex}/ 是否出现 CURRENTMANIFEST-**.sst*.log(与 第 6 篇 布局一致)。
  2. Checkpoint 目录./flink-data/checkpoints 下是否按 checkpoint ID 增长;增量模式下多次 checkpoint 后,单次 checkpointedSize(REST API checkpoints 或 Web UI)应 小于 du -sh 的 RocksDB 本地占用——stream/12 第 5.3 节机制推论。
  3. RocksDB LOG(可选):在 flink-conf 增加 state.backend.rocksdb.log.dir 指向挂载卷,grep Stalling writes / Stopping writes(格式见 第 17 篇db/column_family.cc 源码)。
  4. 对比实验:同一作业改用 HashMapStateBackend 运行,比较 checkpoint 上传 内容形态(Java 序列化 blob vs SST 文件集)——记录相对趋势即可,无需在本文中固定数值

6.5 与 stream/13 联合调优

若 Docker 实验中 syncDuration 持续偏高且 LOG 出现 L0 stall,先按 stream/13 检查 窗口 state 膨胀与 TTL,再按本系列 第 11–12 篇 理解 level0_slowdown_writes_triggermax_background_jobs 的作用边界——改设计优先,调参兜底


七、小结

下一篇 经典故障与排查GetProperty 与 LOG 把 L0 堆积、compaction 落后、Block Cache 未命中、ENOSPC stall 钉到可核对信号。


上一篇Checkpoint、Backup 与 External Ingest

下一篇经典故障与排查

返回 系列目录


参考资料

  1. Apache Flink Documentation, State Backends / Checkpointing / Memory Management(EmbeddedRocksDBStateBackend、incremental checkpoint、managed memory;A 级)。
  2. Apache Flink 源码:flink-statebackend-rocksdbCompositeKeySerializationUtilsRocksIncrementalSnapshotStrategyRocksDBKeyedStateBackend(release-1.20+;A 级)。
  3. Apache Flink 官方博客, Managing Large State in Apache Flink: An Intro to Incremental Checkpointing(2018-01-30;B 级)。
  4. TiKV Documentation, Storage(Region 本地引擎;B 级)。
  5. Apache Kafka Documentation, RocksDB State Store(Kafka Streams;B 级)。
  6. RocksDB Wiki, Checkpoints;RocksDB 9.x 源码 utilities/checkpoint/checkpoint_impl.*(A 级)。
  7. stream-processing 第 12–13 篇(Flink 作业侧;本篇互补不重复)。
  8. 本系列 第 13 篇 Column Family第 15 篇 Checkpoint

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。

2026-07-07 · database / storage

【RocksDB 内核机制】Column Family:共享 WAL 与独立 LSM

从 RocksDB 3.0 引入的 Column Family 出发,拆解共享 WAL、独立 MemTable/SST/Version 链、`ColumnFamilyHandle` 生命周期与 DBOptions/ColumnFamilyOptions 分层;并对照 Flink 多 state 变量到 CF 的映射边界。

2026-07-01 · database / distributed

【流式数据处理】RocksDB State Backend 内核路径

拆解 Flink EmbeddedRocksDBStateBackend 的物理布局:每个 subtask 独立 RocksDB 实例、ColumnFamily 与 KeyGroup 前缀映射、写路径 memtable→WAL→flush→compaction 与 lsm-tree 系列对照、读路径 block cache 与读放大、增量 checkpoint 与全量 snapshot 的 IO 差异。


By .