TiKV 事务与存储层(src/storage)维护指南:架构、调度、MVCC、动态配置与运维信号
【免费下载链接】tikvDistributed transactional key-value database, originally created to complement TiDB项目地址: https://gitcode.com/GitHub_Trending/ti/tikv
本文以 TiKV 仓库的 storage 维护指南 为核心骨架,系统讲解src/storage这一事务与存储层的职责边界、分层架构、请求执行链路、生命周期、数据模型契约、关键不变量、可观测信号以及变更管理方法。读完本文,你将掌握 TiKV 存储层的代码地图与必读顺序、StorageAPI 与TxnScheduler的协作原理、storage 相关配置参数(含动态下发机制)的完整语义,以及排查回调丢失、latch 卡死、MVCC 跨 CF 漂移、动态配置未生效等典型故障的入手点。
目的与范围:src/storage 是什么
src/storage是 TiKV 的事务与存储层,也是仓库中最重要的维护面之一。从 模块文档 可以看到,它承担了"把高层事务命令降低为对持久存储的底层(raw key-value)交互"这一核心职责。它拥有并维护:
StorageAPI:面向 RPC 层的顶层事务/存储入口;- 事务命令调度:命令准入、latch 串行化、任务追踪与 worker 交接;
- MVCC(多版本并发控制):读取、扫描、冲突检测与写入侧变更处理;
- raw KV 操作:raw 键值 API 及其可选的 MVCC 封装;
- latches 与锁等待:调度器侧冲突串行化、存储侧锁等待契约;
- 流控(flow control):写压力管理;
- 存储相关动态配置:在线配置的解析、校验与运行时副作用下发。
从架构归属看,src/storage向下通过Engine抽象(见 engine_traits)与持久化引擎交互,向上被 src/server/service/kv.rs 这一主要调用方承接,写路径经 raftkv 桥接 进入 raftstore。
架构视图
分层视图
自顶向下,src/storage分为五层:
- 顶层
StorageAPI(mod.rs); - 命令调度器(txn/scheduler.rs);
- MVCC 读写逻辑(mvcc/);
- raw KV 逻辑(raw/);
- 锁等待与流控(lock_manager/ 与 txn/flow_controller/)。
请求执行视图
一次请求的典型执行链路为:
- RPC 边界调用
Storage(主要调用方是 src/server/service/kv.rs); - 调度器(scheduler)准入命令:获取 latch、登记任务、按优先级与资源组策略排队;
- MVCC/raw 执行发生在快照(snapshot)与引擎(engine)之上;
- 回调(callback)最终完成并返回 RPC 或更高层。
从 txn/scheduler.rs 的模块注释可以印证:每个 store 有一个TxnScheduler,运行在单线程事件循环中,命令执行被委托给 worker 线程池;scheduler 只保证命令级别对重叠行的串行访问,事务级语义由客户端库实现的事务协议保证。
进程生命周期与启动时序
Storage在服务器启动时构造,且必须满足依赖顺序:引擎(engines)、读池(read pools)、并发管理器(concurrency manager)、配额限流器(quota limiter)以及相关运行时辅助组件先就绪,之后才构建Storage。启动后,运行时配置管理器(runtime config managers)通过 config_manager.rs 更新存储侧行为。关闭阶段必须保持回调与 worker 的所有权安全,直到所有强引用消失。
代码层面有几个具体的运行时锚点:
- storage 构造:
Storage::from_engine(mod.rs)接收 engine、config、read_pool 等构建整个协调层;Storage 结构体 持有engine、sched(TxnScheduler)、read_pool、concurrency_manager、quota_limiter、resource_manager、resource_tag_factory等成员,并通过refs: Arc<AtomicUsize>做引用计数——线程池与 worker 只有在引用归零后才停止(见Clone/Drop实现,mod.rs)。 - scheduler 执行:位于 txn/scheduler.rs。
- 动态配置副作用:位于 config_manager.rs 的
dispatch方法。
数据模型与元数据契约
src/storage依赖若干严格的数据与元数据契约:
- MVCC 数据模型:横跨 default、lock、write 三个列族(CF)。lock CF 记录未提交的锁,write CF 记录已提交写入的版本指针与提交时间戳,default CF 保存实际数据或短值(
SHORT_VALUE_MAX_LEN,见 mvcc/mod.rs)。 - 调度器任务元数据与 latch 所有权:
TaskContext持有lock、cb(回调)、pr(ProcessResult)以及一个owned: AtomicBool,用于保证回调与结果只能被一个线程安全取走(scheduler.rs)。 - 锁等待元数据与 token:
LockDigest、DiagnosticContext、KeyLockWaitInfo与WaitTimeout定义在 lock_manager/mod.rs,超时在 protobuf 中编码为 i64:0 表示默认超时,负值表示不等待。 - raw KV 编码与 API 版本:raw 层按 API 版本选择存储实现,
RawStore是V1/V1Ttl/V2三个变体的枚举(raw/store.rs),分别对应ApiV1、ApiV1Ttl、ApiV2的KvFormat。 - 资源控制元数据:调度过程中消费资源组(resource group)与任务元数据(
TaskMetadata、ResourceTagFactory)。
高风险契约
维护时最需要小心的契约包括:
- MVCC 跨 default/lock/write CF 的关系一致性;
ProcessResult与回调完成语义:ProcessResult定义在 txn/mod.rs,包含Res、MultiRes、PrewriteResult、MvccKey、Locks、TxnStatus、NextCommand、Failed、PessimisticLockRes、SecondaryLocksStatus、RawCompareAndSwapRes等变体,是调度执行结果回传的唯一通道;TxnStatusCache与 max-ts 相关假设(容量默认40_000 * 128,见 config.rs);- raw KV 的 API 版本与 TTL 规则(定义在 config.rs 的
api_version/enable_ttl校验中)。
代码地图:Start Here 与必读顺序
文档给出的"从这开始"清单(均以仓库根目录为起点):
- src/storage/mod.rs
- src/storage/txn/scheduler.rs
- src/storage/txn/mod.rs
- src/storage/txn/commands/mod.rs
- src/storage/mvcc/mod.rs
- src/storage/txn/store.rs
- src/storage/raw/store.rs
- src/storage/config.rs
- src/storage/config_manager.rs
推荐的必读文件顺序为:
- src/storage/mod.rs
- src/storage/config.rs
- src/storage/txn/mod.rs
- src/storage/txn/scheduler.rs
- src/storage/txn/commands/mod.rs
- src/storage/mvcc/mod.rs
- src/storage/mvcc/reader/reader.rs
- src/storage/txn/store.rs
- src/storage/lock_manager/mod.rs
- src/storage/config_manager.rs
内部结构详解
顶层 API:Storage
mod.rs 定义了Storage与大量公开操作。从Storage的成员可以看到它的协调者角色:它把 scheduler(TxnScheduler)、read pool、concurrency manager、quota limiter、resource control 组装在一起,对外提供事务与 raw API;max_key_size等配置字段直接参与命令参数校验。模块内还细分了txn(事务命令 → MVCC 抽象)、mvcc(MVCC 实现)、kv(持久化存储抽象)三个层次(mod.rs 模块注释)。
调度器与命令执行
- txn/scheduler.rs 拥有命令准入、latches、任务追踪与 worker 交接。任务槽位数为
1 << 12(4096,见 scheduler.rs)。 - txn/commands/ 定义具体事务命令(prewrite、commit、acquire_pessimistic_lock、gc、cleanup、flashback 等,见 txn/mod.rs 的 actions 导出)。
- txn/task.rs、txn/sched_pool.rs 与 txn/tracker.rs 支撑执行。
- 关于调度池的细节(sched_pool.rs):
- 仅为自定义资源组(customized resource groups)选择优先级队列;仅后台控制仍使用 vanilla 队列,并在池内对匹配的长运行任务进行节流。
- 调度池会从非只读 scheduler 命令中累计 MVCC 读流——包括前台写命令执行过程中执行的读——并在 ticker 与 worker 关闭时的 flush 中,把 region 级
ReadStats上报给 raftstore reporter(local_read_stats/report_read_stats)。需要留意:此路径上关键范围(key ranges)与 bucket 增量不可用。
MVCC
- mvcc/mod.rs 定义 MVCC 错误与公共面。错误类型覆盖
KeyIsLocked、Committed、WriteConflict、Deadlock、TxnLockNotFound、PessimisticLockRolledBack等(mvcc/mod.rs),这些是事务冲突与恢复逻辑的判定基础。 - mvcc/reader/ 拥有读侧:scanner、point getter、冲突检测。
- mvcc/txn.rs 拥有写侧 MVCC 变更处理(
MvccTxn、GcInfo、MAX_TXN_WRITE_SIZE)。
Raw KV
- raw/ 实现 raw 键值 API 及其可选的 MVCC 封装。
RawStore::new按ApiVersion分发到 V1 / V1Ttl / V2 三种内部实现(raw/store.rs):V1 直接基于 snapshot;V1Ttl 额外套一层RawEncodeSnapshot处理 TTL 过期;V2 再叠加RawMvccSnapshot。scan 操作带MAX_TIME_SLICE = 2ms与MAX_BATCH_SIZE = 1024的时间片/批量限制(raw/store.rs)。
等待与流控
- lock_manager/ 定义存储侧锁等待契约与本地等待队列辅助:
LockDigest、DiagnosticContext、KeyLockWaitInfo、WaitTimeout以及 lock_wait_context / lock_waiting_queue。 - 活跃的 waiter-manager 工作者、死锁检测器(deadlock detector)与锁管理器 RPC 服务位于 src/server/lock_manager/。
- txn/flow_controller/ 控制写压力行为。
Max-ts 动态配置
- config.rs 的
MaxTsConfig::validate使用max_drift与cache_sync_interval的有效整毫秒值进行比较,要求max_drift严格大于cache_sync_interval,否则启动报错(config.rs)。 - config_manager.rs 的
dispatch把max_ts.max_drift精确地以ReadableDuration下发到ConcurrencyManager::set_max_ts_drift_allowance(config_manager.rs)。 ConcurrencyManager有意用整毫秒存储该容差,因为 TSO 物理时间戳以毫秒为单位。例如一个合法的配置值15s1us,其有效强制执行容差为15000ms;这是领域边界(domain boundary),而非在线配置截断。这一行为由单元测试test_validate_max_ts_config_uses_effective_milliseconds锁定(config.rs)。
存储配置全景:参数、默认值与校验规则
storage 相关配置在 src/storage/config.rs 中定义,模板注释在 etc/config-template.toml 中可查。以下是关键字段、默认值及其校验逻辑。
顶层配置([storage]段对应字段)
| 字段 | 默认值 | 说明与校验 |
|---|---|---|
data_dir | "./" | 数据目录(不可在线修改) |
engine | "raft-kv" | raft-kv/partitioned-raft-kv(RaftKv2)。校验时会检查kv与tablet子目录是否同时存在:两者同时存在直接报错;若数据目录与 engine 类型不符,则以已存在的数据目录为准自动回退(config.rs) |
gc_ratio_threshold | 1.1 | 已由GcConfig.ratio_threshold取代,仅保留向后兼容 |
max_key_size | 8 * 1024(8KB) | 命令 key 大小上限 |
scheduler_concurrency | 1024 * 512 | 调度器 latch 并发度,上限2 * 1024 * 1024,超限会被钳制并告警(v4.0 起 latch 已优化,不建议设过大) |
scheduler_worker_pool_size | CPU≥16 时 8,否则clamp(1, 4) | 必须大于 0 且不超过max(4, cpu 配额),否则校验失败 |
scheduler_pending_write_threshold | 100MB | 依据 Little's law 的写入积压阈值(按 100MB/s 写速、约 100ms 处理时延估计) |
reserve_space | 5GB | 磁盘满时保留的压缩空间 |
reserve_raft_space | 1GB | raft 日志保留空间 |
enable_async_apply_prewrite | false | 异步 apply prewrite 开关 |
api_version | 1 | 只能为 1 或 2;API V2 强制开启 TTL |
enable_ttl | false | API V2 下必须为true |
background_error_recovery_window | 1h | 后台错误恢复窗口,设为 0 可禁用(遇到此类错误立即 panic) |
ttl_check_poll_interval | 12h | 全量 SST 的 TTL 检查间隔 |
txn_status_cache_capacity | 40_000 * 128(5.12M) | TxnStatusCache 容量上限(128 槽位) |
memory_quota | 256MB | 待执行与执行中 storage 命令(kv_get/kv_prewrite/kv_commit 等)的内存配额;校验时若小于 pending-write 阈值会自动抬升 |
flow_control | 见下 | 流控子模块 |
block_cache | 见下 | 共享 block cache 子模块 |
io_rate_limit | 见下 | IO 限速子模块 |
max_ts | 见下 | max-ts 偏差控制子模块 |
memory_quota的 256MB 默认值背后有工程依据(config.rs):单条命令内存约等于 1KB KV 对 + 约 448 字节 Command + 约 6184 字节执行 future,256MB 约可支撑 3.5 万个并发执行命令或 18.2 万个排队命令;单节点默认配置下 TPCC prepare(--threads 500)实测内存约 50MB。
[storage.flow-control]
| 字段 | 默认值 | 说明 |
|---|---|---|
enable | true | 启用后禁用 kvdb 与 raftdb 的 write stall(memtable 除外),改为在 scheduler 层限流,raftstore 与 apply 不再被阻塞 |
soft-pending-compaction-bytes-limit | 192GB | 达到后开始以ServerIsBusy拒绝部分写请求 |
hard-pending-compaction-bytes-limit | 1024GB | 达到后拒绝全部写请求 |
memtables-threshold | 5 | kvdb 不可变 memtable 数达到该值开始流控 |
l0-files-threshold | 20 | kvdb L0 SST 文件数达到该值开始流控 |
模板注释见 etc/config-template.toml,流控配置支持动态修改,write_into_metrics会把阈值同步到CONFIG_FLOW_CONTROL_GAUGE指标(config.rs)。
[storage.block-cache]
capacity:共享块缓存大小。raft-kv 默认约为系统可用内存的 45%,partitioned-raft-kv 约为 30%(见 config-template.toml);单机多实例部署必须显式配置以避免 OOM。缓存总是共享(shared字段已废弃)。- 其他参数:
num-shard-bits = 6(容量过小时adjust_shard_bits会自动下调)、strict-capacity-limit = false、high-pri-pool-ratio = 0.8、low-pri-pool-ratio = 0.2、memory-allocator = "nodump"(jemalloc nodump,用于避免缓存内存被 dump)。
[storage.io-rate-limit]
| 字段 | 默认值 | 说明 |
|---|---|---|
max-bytes-per-sec | 0MB | 每秒最大 IO 字节数;0 表示不限速,建议设为磁盘厂商标称的最优 IO 带宽 |
mode | "write-only" | 目前仅支持write-only模式,其他模式校验报错 |
strict | false | 关闭时高优先级 IO 只计数不限速;多租户场景应开启 |
各*_priority | 见代码 | 前/后台 IO 优先级:前台读写、flush 为 High,compaction 为 Low,其余见 config.rs |
校验规则(config.rs):IOType::Other优先级会被强制为 High(该类型可能包含关键 IO);gc_priority会被强制与前台写优先级一致,避免优先级反转。
[storage.max-ts]
| 字段 | 默认值 | 说明 |
|---|---|---|
max-drift | 60s | 允许 max_ts 偏离 PD TSO 的最大值 |
cache-sync-interval | 15s | 从 PD 刷新 max_ts 上限的间隔(不可在线修改) |
action-on-invalid-update | "panic" | 收到非法 max_ts 更新时的动作(合法值由ActionOnInvalidMaxTs定义) |
校验要求max_drift的有效毫秒值必须大于cache_sync_interval的有效毫秒值(config.rs),两者以"有效整毫秒"对齐运行时 TSO 的毫秒精度。
动态配置下发:config_manager.rs
运行时变更由 config_manager.rs 的StorageConfigManger::dispatch处理,它把配置修改分发到具体运行时对象(这也是"改配置必须同步改副作用"这一维护原则的落点):
block_cache.capacity→ConfigurableDb::set_shared_block_cache_capacity,并写CONFIG_ROCKSDB_CF_GAUGE;ttl_check_poll_interval→ 调度TtlCheckerTask::UpdatePollInterval;flow_control→ 先FlowController::update_config,再按enable开关批量设置各 CF 的disable_write_stall并调用FlowController::enable;scheduler_worker_pool_size→TxnScheduler::scale_pool_size;memory_quota→TxnScheduler::set_memory_quota_capacity;io_rate_limit→ 更新IoRateLimiter的速率与各IoType优先级;max_ts.action_on_invalid_update→ConcurrencyManager::set_action_on_invalid_max_ts_update;max_ts.max_drift→ConcurrencyManager::set_max_ts_drift_allowance。
关键不变量(Critical Invariants)
维护src/storage时必须始终维持以下不变量:
- 命令回调必须恰好完成一次,且携带正确的错误/结果语义(对应
ProcessResult与回调契约); - latch 所有权必须串行化冲突命令且不使调度器死锁;
- MVCC 读写必须维持 lock、write、default CF 之间的关系;
- region 边界、快照上下文、flashback/max-ts 安全必须持续生效;
- max-ts 关系校验必须与运行时 TSO 强制执行使用相同的整毫秒精度;
- 内存配额与 pending-write 阈值必须保持运维上有效。
可观测性与运维信号
开始故障排查时,建议从以下位置入手:
- src/storage/metrics.rs
- src/storage/txn/scheduler.rs
- src/storage/lock_manager/
- src/storage/config_manager.rs
可观测信号包括:
- scheduler 延迟、latch 等待与 pending-write 信号:
SCHED_HISTOGRAM_VEC_STATIC等热路径指标(scheduler.rs); - MVCC 冲突、读与 GC 相关指标;
- 流控与内存配额行为:
CONFIG_FLOW_CONTROL_GAUGE暴露阈值,配额限流器负责执行; - 锁等待与死锁诊断:
DiagnosticContext携带 key、资源组 tag 与 tracker,用于聚合同一语句产生的锁等待详情(lock_manager/mod.rs); - PD 读流报告:包含前台写命令执行的读;当 PD 读字节数与 read-pool 工作量不匹配时,检查 txn/sched_pool.rs 的读流累计与上报逻辑。
TopSQL / 资源计量的逻辑 IO 归属细节
当resource-metering.enable-network-io-collection启用时,资源计量/TopSQL 记录逻辑 IO;当resource-metering.enable-detailed-io-collection也启用时,逻辑读与逻辑写被独立选取,前台 SQL 请求的 RocksDB PerfContext 增量被记录为rocksdb_block_read_count。该字段向下游read_iops维度提供相对归因,并不是设备级 IOPS 测量值。
存储命令边界在 metrics.rs 中拥有对该归因的所属权,适用于只读命令与写命令的读阶段。特别地:
- 事务写与
raw_compare_and_swap必须保留Storage的 PerfContext,因为 CAS 在决定是否写入前要先读取旧值; - 恢复的悲观锁(pessimistic-lock)批次可能包含来自多个请求的工作,TopSQL 有意使用合成命令的第一个上下文作为整个批次的代表(包括逻辑写与单次 detailed-I/O PerfContext 观测),因此混合 tag 批次的归因是近似值,以避免在锁唤醒路径上做逐项工作;
- 不要把 raftstore apply/store 写 worker 的活动归因到请求上:这些路径使用只写 PerfContext 指标,且可能批量处理多个请求的工作。
变更管理指导
- 任何对
StorageAPI、回调语义、MVCC 契约或动态配置副作用的改动,都应在同一 patch 内同步更新本维护指南; - 存储语义或错误映射变化时,应审计 server 与 raftstore 桥接层;
- 如果修改配置值却没有同步更新 config_manager.rs 的副作用,该改动通常是不完整的。
变更影响矩阵
| 变更类型 | 需要检查的范围 |
|---|---|
公开StorageAPI 或回调变更 | mod.rs、txn/scheduler.rs,以及 src/server 中面向 RPC 的调用方 |
| 调度器、latch 或任务流变更 | txn/scheduler.rs、txn/task.rs、txn/sched_pool.rs 与指标 |
| MVCC 读写或冲突变更 | mvcc/、txn/store.rs 与事务命令 |
| raw KV 变更 | raw/、API 版本处理与 coprocessor_v2 存储适配器 |
| 锁等待或流控变更 | lock_manager/、txn/flow_controller/,以及 src/server 中的运行时工作者 |
| 动态配置变更 | config.rs、config_manager.rs 与运行时副作用消费方 |
审查清单
提交涉及存储层的改动前,请逐项确认:
- 是否影响
StorageAPI 行为或回调契约? - 是否触及 txn/scheduler.rs、latches 或锁等待队列?
- 是否修改 MVCC 冲突检测、锁解析或 commit-ts 规则?
- 是否改变与插件 coprocessor 或外部 API 共享的 raw KV 行为?
- 是否改变必须同时适用于
raftkv与raftkv2桥接层的快照或写入假设(src/server/raftkv/mod.rs 与 src/server/raftkv2)? - 动态配置是否需要 config-manager 副作用?
- 是否改变资源控制集成、流控或配额限流器行为?
测试与可观测性
- MVCC、事务命令、latches、锁等待、raw MVCC 与调度器辅助逻辑拥有大量内联单元测试(例如 config.rs 的 validate 测试 覆盖 engine 类型回退、max-ts 毫秒精度比较与 shard bits 调整);
- 集成测试多归属 components/test_storage;
- 调度器、MVCC、读池与流控的热路径指标遍布各模块。
常见故障模式
- 回调从未调用或调用两次:破坏
ProcessResult完成语义,会导致请求悬挂或结果重复; - latch 释放 bug:造成命令卡死或乱序;
- MVCC 跨 CF 写/读漂移:lock/write/default 关系被破坏;
- 从底层错误中提取 region 错误不正确:影响上层重试与错误上报;
- 动态配置只更新了内存中的 config 值、未应用到运行时对象:典型的 config_manager 副作用缺失;
- 锁等待唤醒逻辑导致饥饿或漏唤醒:影响悲观事务的并发与延迟。
阅读地图与配套文档
建议按 1 → 10 的顺序阅读代码地图中的必读文件(见上文"代码地图"一节),配套文档包括:
- repo-overview.md
- src/server.md
- components/raftstore.md
- PERFORMANCE_CRITICAL_PATH.md
术语表
- MVCC:multi-version concurrency control,基于 default/lock/write CF 的多版本并发控制;
- Latch:调度器侧用于冲突命令串行化的原语;
- ProcessResult:调度执行结果,通过回调返回;
- Flow control:压力管理逻辑,用于减缓或门控写密集活动。
相关组件
- src/server/service/kv.rs:
Storage的主要调用方; - src/server/raftkv/mod.rs:把写请求桥接进 raftstore;
- src/server/lock_manager/:围绕存储侧等待队列契约的活跃锁管理器工作者与死锁检测服务;
- components/resource_control 与 components/resource_metering:直接与调度和计量集成。
【免费下载链接】tikvDistributed transactional key-value database, originally created to complement TiDB项目地址: https://gitcode.com/GitHub_Trending/ti/tikv
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考