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

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

文章导航

分类入口
databasestorage
标签入口
#rocksdb#column-family#wal#lsm-tree#column-family-handle#flink#options#superversion

目录

第 12 篇 把 compaction 并发与 RateLimiter 钉在 CompactionJob 线程池上。LevelDB 时代「整库一棵 LSM + 一份 MANIFEST」在 Flink state、TiKV Region 本地引擎等场景不够用:同一 DB 实例内需要多组 comparator、独立 compaction 策略、可单独 drop 的逻辑分区,又不能牺牲跨分区原子写。

RocksDB 3.0 引入 Column Family(CF):每个 key-value 恰属一个 CF;WAL 整库共享MemTable 与 SST 文件按 CF 独立。本文是系列 第 13 篇,沿 Wiki Column Familiesdb/column_family.* 源码路径说明机制、ColumnFamilyHandle 语义、选项分层,以及 Flink 如何把多种 state 变量映射到不同 CF(Flink 侧细节见 stream/12,本系列第 16 篇补内核侧对照)。

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

篇目 核心内容
第 12 篇 · 并发 Compaction 与 Rate Limiter CompactionJob 线程池
第 13 篇 · Column Family 共享 WAL、独立 LSM、Flink CF 映射
第 14 篇 · 事务与 OCC OptimisticTransactionDB
第 15 篇 · Checkpoint 与 Ingest 硬链接快照

版本锚定:RocksDB 9.xfacebook/rocksdb,Wiki 与 include/rocksdb/*.h);Flink 1.20+(CF 映射引用官方 State Backends 文档,B 级)。本篇不粘贴未实测的 WAL 回收延迟数字。


一、问题:单 LSM 树不够用时拆什么

LevelDB 只有 default 一棵 LSM:comparator、memtable 大小、compression、compaction 策略全库统一。第 3 篇 已把 CF 标为相对 LevelDB 的核心 diff 之一。生产嵌入的典型诉求:

诉求 单 CF 的困难 CF 提供的抽象
不同 state 变量隔离 scan 同一 Iterator 扫全库 每 CF 独立 LSM,点查/范围扫限定在 CF 内
按模块调 compaction 全局 compaction_style ColumnFamilyOptions per-CF
逻辑删除一组数据 逐 key Delete DropColumnFamily 快速丢弃整棵子树
跨模块原子更新 只能靠 WriteBatch 仍用 WriteBatch,但可一次写多个 CF

CF 不是 多进程隔离,也 不是 独立 WAL 目录;它是 同一 DB 指针下的逻辑分区,磁盘上仍共用一个 DB 目录(MANIFEST 全局一份,见 第 6 篇)。


二、共享 WAL、独立 LSM:磁盘与内存布局

Wiki Column Families 的实现要点(A 级):

Column Families share the write-ahead log and don’t share memtables and table files.

flowchart TB
  subgraph DB["单个 RocksDB DB 实例"]
    WAL["共享 WAL (*.log)"]
    subgraph CF1["Column Family: default"]
      M1["MemTable / imm"]
      V1["Version L0..Ln"]
      S1["*.sst"]
    end
    subgraph CF2["Column Family: count"]
      M2["MemTable / imm"]
      V2["Version L0..Ln"]
      S2["*.sst"]
    end
    MAN["全局 MANIFEST"]
  end
  Write["WriteBatch 多 CF 写"] --> WAL
  WAL --> M1
  WAL --> M2
  M1 --> S1
  M2 --> S2
  V1 --> MAN
  V2 --> MAN

一次 DB::Write 把整批 WriteBatch 追加到 当前活跃 WAL;recovery 时按 batch 里的 CF id 把记录 replay 到 各自 CF 的 MemTable第 4 篇)。读路径上,每个 CF 持有独立 SuperVersion(mem + imm + Version),第 7 篇GetImpl 需传入 ColumnFamilyHandle* 才能定位正确 LSM。

原子写跨 CFWriteBatch 可含 {cf1,k1,v1}{cf2,k2,v2};要么整批 commit,要么都不生效——依赖 单 WAL 顺序 + 单 sequence 空间,而非 per-CF WAL。


三、WAL 回收与 max_total_wal_size

共享 WAL 带来 回收耦合:某个 CF flush 后,WAL 里仍可能有 其他 CF 未落盘 的数据,旧 WAL 不能删。

Wiki 说明(A 级):

工程含义:Flink 多 CF 作业若某一 state 写入很少、长期不 flush,会 拖住 WAL 体积,间接影响 checkpoint 与磁盘占用——这与 stream/13 状态调优 里「多 CF 共享 WAL」的调参点一致。本篇只钉机制,不展开 Flink 指标。


四、ColumnFamilyHandle 与 Open 语义

ColumnFamilyHandle 类似 已打开的 CF 文件描述符(Wiki 用语)。要点:

API / 行为 说明
DB::Open(..., vector<ColumnFamilyDescriptor>, handles) 读写打开须 列出 DB 中现有全部 CF,否则 InvalidArgument
DB::OpenForReadOnly 可只打开 CF 子集
DB::ListColumnFamilies 静态方法,枚举磁盘上 CF 名
CreateColumnFamily / DropColumnFamily 在线增删;Drop 后若仍有 Handle 引用,数据延迟删除
delete handle 关闭前须释放 所有 Handle,再 delete db

WriteBatch::Put(ColumnFamilyHandle*, ...)Get/Iterator/NewIterator 等均需显式 CF 参数。默认 CF 名为 "default";仅用旧 API(无 CF 参数)的代码等价于 只写 default(Wiki Backward compatibility)。

Drop 语义DropColumnFamily 后 Handle 仍可对 已 drop CF 读写,直到最后一个 Handle 释放——便于滚动升级时排空引用,但运维上容易误判「已删仍在用」。


五、选项分层:DBOptionsColumnFamilyOptions

RocksDB 3.0 起把 Options 拆成两层(include/rocksdb/options.h,A 级):

结构 作用域 典型字段
DBOptions 整库 max_background_jobswal_dirrate_limiterstatistics
ColumnFamilyOptions 单 CF write_buffer_sizecompaction_stylecomparatortable_factory
Options 继承二者 单 CF 或 default CF 的便捷写法

ColumnFamilyDescriptor = CF 名 + ColumnFamilyOptions,在 Open 时成批传入。不同 CF 可配置 不同 leveled/universal 策略、不同 bloom、不同 TTL第 10–11 篇 的 picker 按 CF 独立运行)。

Comparator 约束:同一 DB 内各 CF 可以 使用不同 comparator(按 CF 配置);SstFileWriter / external ingest 时 comparator 必须与目标 CF 完全一致第 15 篇)。


六、MANIFEST 与多 CF 版本链

第 6 篇 已述:MANIFEST 全局一份VersionEditkColumnFamily 区分 CF;VersionSet::ColumnFamilySet 维护 ColumnFamilyData → Version* 链表。

Flush / compaction 提交时,LogAndApply 可一次写入 多 CF 的 Edit 组kInAtomicGroup),保证 跨 CF 的元数据视图一致切换。读侧每个 CF 的 SuperVersion 独立切换;不存在「一个 CF 看到新 SST、另一个仍见旧 Version」的半套状态(在同一 GetAndRefSuperVersion(cf) 调用内)。


Flink EmbeddedRocksDBStateBackend 的映射规则(Apache Flink 文档 State BackendsPredefined Per-ColumnFamily Options,B 级):

当前实现中,算子里的每一种 registered state 对应一个 RocksDB Column Family。

示例:同一窗口算子注册 ValueStateDescriptor("count")MapStateDescriptor("user-last-seen") → 该 subtask 的 RocksDB 至少 两个业务 CF(另加 RocksDB 内置 default,通常不参与 Flink 状态)。

flowchart LR
  OP["WindowOperator"]
  subgraph RDB["subtask RocksDB"]
    CFc["CF: count"]
    CFm["CF: user-last-seen"]
    WAL["共享 WAL"]
    BC["共享 Block Cache / WBM"]
  end
  OP --> CFc
  OP --> CFm
  WAL --> CFc
  WAL --> CFm

与内核机制的对应关系:

Flink 概念 RocksDB 机制
多种 state 变量 多 CF,独立 LSM
KeyGroup 前缀 user key 编码,不是 CF 边界(stream/12
增量 checkpoint 上传 不可变 SST;依赖 CF 内 SST 与全局 MANIFEST 语义(第 15 篇 Checkpoint)
RocksDBOptionsFactory 按 CF 名返回不同 ColumnFamilyOptions

分工:stream/12 讲作业布局与 KeyGroup;本篇讲 为何 Flink 选 CF 而非 key 前缀分 state——便于 per-state TTL compact filter、savepoint 元数据对齐、以及缩小单次 Iterator 扫描范围。第 16 篇将把 TiKV / Kafka Streams 一并对照。


八、API 示例:Open 与跨 CF 原子写

#include "rocksdb/db.h"
#include "rocksdb/options.h"

DBOptions db_opts;
std::vector<ColumnFamilyDescriptor> cfs{
    {"default", ColumnFamilyOptions{}},
    {"metrics", ColumnFamilyOptions{}},
};
std::vector<ColumnFamilyHandle*> handles;
rocksdb::DB* db;
auto s = DB::Open(db_opts, "/tmp/cf_demo", cfs, &handles, &db);

WriteBatch batch;
batch.Put(handles[0], "k1", "v1");
batch.Put(handles[1], "m1", "100");
WriteOptions wopts;
s = db->Write(wopts, &batch);  // 单 WAL 记录,原子跨 CF

db->DropColumnFamily(handles[1]);
for (auto* h : handles) delete h;
delete db;

(示例仅说明 API 形状;路径与错误处理从简。)


九、与 LevelDB / 单 CF 心智的对照

维度 LevelDB RocksDB 多 CF
WAL 1 份 仍 1 份,全 CF 共享
MemTable / SST 1 套 每 CF 一套
MANIFEST 1 份 1 份,Edit 带 CF id
Open 无 CF 列表 读写须枚举已有 CF
原子批写 WriteBatch WriteBatch + 多 CF handle

十、小结


上一篇并发 Compaction 与 Rate Limiter

下一篇事务与 OptimisticTransactionDB

返回 系列目录


参考资料

  1. RocksDB Wiki, Column Families(共享 WAL、flush 与 WAL 删除、max_total_wal_size;A 级)。
  2. RocksDB 9.x 源码:db/column_family.hdb/column_family.ccColumnFamilyDataSuperVersion);db/version_set.ccColumnFamilySetLogAndApply);include/rocksdb/options.hDBOptionsColumnFamilyOptions)(A 级)。
  3. RocksDB 9.x 源码:include/rocksdb/db.hOpenCreateColumnFamilyDropColumnFamilyListColumnFamilies)(A 级)。
  4. Apache Flink 文档, State BackendsPredefined Per-ColumnFamily Options(每种 registered state 对应一个 CF;B 级)。
  5. stream-processing 第 12 篇:RocksDB State Backend第 6 篇 MANIFEST第 4 篇 WAL

同主题继续阅读

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


By .