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

【TiKV / HTAP 内核】HTAP / 分布式 KV 全景:TiDB–TiKV–PD–TiFlash 四件套

文章导航

分类入口
databasestorage
标签入口
#tikv#tidb#tiflash#pd#htap#multi-raft#region#distributed-kv

目录

一次 UPDATE orders SET status = 'paid' WHERE id = 42 在 TiDB 集群里要经过什么,才能既保证事务提交后立刻在其他节点可读,又不需要整张表停机搬迁?答案分散在四个组件里:TiDB 把 SQL 变成 Key-Value 请求并路由到正确节点;TiKV 用 Raft 复制这个 Key-Value 请求并落盘;PD 决定这份数据现在归哪几个节点管、要不要搬;TiFlash 在旁路异步把同一份数据变成列存,供分析查询秒级可读。四个组件各管一段,单独看某一个都不足以回答「这次写为什么这么快/这么慢」。

常见的困惑是把这套系统当成「一个分布式 MySQL」,出问题时习惯性地怀疑 SQL 层或索引。但 TiDB 的很多延迟异常、可用性异常,根子在 Region 怎么切、Raft 组怎么复制、PD 怎么调度——这是本系列要拆的那一层,SQL 层只在第 13 篇露一次面。

本文是「TiKV / HTAP 内核」系列第 1 篇(共 18 篇),只做三件事:

  1. 说清 TiDB、TiKV、PD、TiFlash 四个组件各自的抽象边界,避免把它们当成同一个东西的不同名字。
  2. 把这一层与站内已有的 RocksDB 内核Raft 解读HTAP 新范式分布式 KV 选型对照 画出分工图。
  3. 给出本系列 18 篇的路线图。

本文是「TiKV / HTAP 内核」系列第 1 篇(共 18 篇)。→ 系列目录

篇目 核心内容
第 1 篇 · 全景 四件套分工、系列路线
第 2 篇 · Key 编码与 MVCC user key、start_ts、三 CF
第 3 篇 · Region 模型 range、epoch、peer

版本锚定:TiKV / PD / TiFlash 7.x–8.x 官方文档(TiKV ArchitectureTiKV in 5 Minutes);Huang et al., TiDB: A Raft-based HTAP Database, PVLDB 13(12), 2020(DOI: 10.14778/3415478.3415535)。本文不复述 Raft 选举/日志复制的教科书内容,也不重拆 RocksDB 的 DBImpl,两者分别见对应系列。


一、四件套:谁做什么,谁不做什么

一次 SQL 请求从客户端出发,依次穿过下面四层。表格先给结论,后面逐层展开。

组件 角色 核心抽象 不做什么
TiDB 无状态 SQL 层 逻辑表、执行计划、事务协调者 不持久化数据,不做 Raft 复制
TiKV 分布式事务 KV Region、Multi-Raft、MVCC 三 CF 不理解 SQL、不做列存
PD(Placement Driver) 元数据与调度中枢 Region 路由表、TSO、调度算子 不存业务数据,不参与 Raft 日志复制
TiFlash 列存分析副本 Raft Learner、列存文件 不参与 Raft 选举/提交、不写 OLTP 路径

1.1 TiDB:把 SQL 翻译成 Key-Value 请求

TiDB 节点本身无状态:不持久化任何用户数据,重启或扩容不涉及数据搬迁。它做三件事——解析 SQL、生成分布式执行计划、把计划中的每一步转成对 TiKV 的 Key-Value 读写请求(点查、范围 scan、或下推到 Coprocessor 的聚合算子)。表和索引在 TiKV 里没有「表」这个物理概念,只是一段编码规则固定的 Key 区间(第 2 篇展开)。

TiDB 还承担 Percolator 式分布式事务的协调者角色:选主键、发起 prewrite/commit(第 10–11 篇)。本系列不重写 TiDB 优化器与执行器,第 13 篇只画出「SQL 计划如何落到具体 Region」这条边界线。

1.2 TiKV:Region 与 Multi-Raft 是全部秘密

TiKV 把整个集群的 Key 空间看成一个全局有序的大 Map,按 Key 的字典序切成一段一段的 Region(第 3 篇)。每个 Region 的多个副本组成一个独立的 Raft 组——这是 Multi-Raft(第 4 篇):一个 TiKV 集群同时运行着成千上万个并发的、彼此独立的 Raft 组,而不是像 etcd 那样全集群共享一个 Raft 组。

单个 Region 内部,数据落在本地的 RocksDB 实例里,用 CF_DEFAULT / CF_LOCK / CF_WRITE 三个 Column Family 承载 MVCC(第 2 篇)。这一层与 RocksDB 内核系列 讲的单机 LSM 完全复用,TiKV 在其上加的是分布式路由和 Raft 复制。

1.3 PD:知道每个 Region 在哪、该往哪搬

PD 不存业务数据,只存元数据:每个 Region 的 range、副本分布在哪些 TiKV 节点、谁是 Leader。TiDB 每次访问 TiKV 前先向 PD 问路由(有本地缓存,失效才重新问),TiKV 节点周期性向 PD 发心跳汇报自身状态,PD 根据心跳生成调度算子(transfer-leader、add-peer 等),下发给对应 TiKV 执行(第 7–8 篇)。

PD 还是时间戳权威(TSO,第 9 篇):所有事务的 start_ts / commit_ts 都由 PD 统一分配,保证全局单调递增,这是 Percolator 模型能在分布式环境里工作的前提。

1.4 TiFlash:Raft Learner,不是「第二个存储引擎」

TiFlash 的关键设计不是「另起一个列存数据库同步数据」,而是把自己接入到 TiKV 现有的 Raft 组里,扮演 Learner 角色(第 14 篇):它接收 Leader 复制过来的 Raft 日志,把行格式数据转成列存,但不参与 Raft 的日志提交投票和 Leader 选举。Huang et al.(PVLDB 2020)称这是把「replicated state machine 的日志」同时喂给行存和列存两种状态机实现——这是 TiDB 论文对 HTAP 问题给出的核心方案,而不是简单的 CDC 同步。

flowchart LR
  client["SQL Client"]
  tidb["TiDB<br/>stateless SQL layer"]
  pd["PD<br/>metadata + scheduling + TSO"]
  subgraph tikv_cluster ["TiKV cluster"]
    r1["Region A<br/>Raft group"]
    r2["Region B<br/>Raft group"]
  end
  tiflash["TiFlash<br/>Raft learner, columnar"]

  client --> tidb
  tidb -->|"route query"| pd
  tidb -->|"KV read / write"| r1
  tidb -->|"KV read / write"| r2
  pd -->|"heartbeat + operator"| r1
  pd -->|"heartbeat + operator"| r2
  r1 -.->|"raft log (learner)"| tiflash
  r2 -.->|"raft log (learner)"| tiflash
  tidb -->|"analytical query"| tiflash

二、与站内已有系列的分工

TiKV 这套架构涉及的三块基础知识,本站已经各自有系列讲过,本系列不重复:

话题 已有内容怎么讲的 本系列怎么用
单机 LSM、Column Family、事务 API RocksDB 内核 第 1–16 篇拆 DBImpl、Compaction、WriteBatch 第 2、5 篇直接引用 CF 语义,只讲 TiKV 在其上加的 MVCC 编码
Raft 选举、日志复制、安全不变量 Raft 解读 用 Figure 8 手填表讲清楚安全性证明 第 4–6 篇只引用不变量结论(如「已提交日志不会被覆盖」),不重讲选举流程
HTAP 范式:行列双引擎 / 一体存储 / Lakehouse 三条路线,新鲜度分档 db-frontier/12 HTAP 新范式 对比 TiDB+TiFlash、SingleStore、Lakehouse 本系列只钻 TiDB+TiFlash 这一条路线的工程实现:Region、Learner、apply 路径
分布式 KV 系统选型 distributed/39 KV 存储对比 横向比较 etcd/TiKV/FoundationDB 的定位差异 第 18 篇回链,收束到「什么场景选 TiKV 而不是 etcd/FDB」

一句话概括这条分工线:RocksDB 讲单 Region 内部怎么存;Raft 系列讲一个 Raft 组怎么保证安全;db-frontier/12 讲 HTAP 这个问题域里有哪些设计路线;本系列讲 TiKV 具体怎么把这三者粘成一个生产系统

flowchart TB
  lsm["rocksdb series<br/>single-node LSM, CF, txn"]
  raft["raft-explained<br/>consensus safety invariants"]
  htap["db-frontier 12<br/>HTAP design space"]
  this["This series<br/>TiKV engineering: Region, Multi-Raft,<br/>raftstore, PD, Percolator, TiFlash learner"]
  kvcompare["distributed 39<br/>etcd / TiKV / FDB selection"]

  lsm --> this
  raft --> this
  htap --> this
  this --> kvcompare

常见误解

  1. 「TiKV 就是分布式 RocksDB」。RocksDB 只解决单机存储;TiKV 在此之上加的 Region 切分、Multi-Raft 复制、跨节点事务协调才是分布式系统真正的复杂度所在。把 TiKV 的问题全部归因到 RocksDB 参数,往往查错方向(第 5–6 篇会具体展开这条边界)。
  2. 「PD 只是个配置中心」。PD 承担的调度决策(该不该 split、该不该迁 leader)直接决定集群负载均衡效果和尾延迟,其调度算子的生成逻辑本身是一套持续运行的控制系统(第 7–8 篇)。
  3. 「TiFlash 是 TiKV 数据同步到另一个数据库」。TiFlash 复制的是 Raft 日志、而不是表级 CDC;它以 Raft Learner 身份直接参与 Region 的复制拓扑,这决定了它的新鲜度保证与传统「异步 ETL 到数仓」完全不同(第 14–15 篇展开)。

三、一个写请求的完整路径

前两节分别讲了「四件套各管什么」和「与哪些系列分工」,这一节把它们串成一条具体的路径,作为后续篇目反复引用的坐标系。以 UPDATE t SET v = 1 WHERE id = 42 为例:

  1. TiDB 解析与路由:TiDB 把这条 SQL 解析成执行计划,算出 id = 42 这一行落在哪个 Key(按第二节提到的行编码规则)。TiDB 本地缓存里如果没有这个 Key 所在 Region 的路由信息,先向 PD 查询(第 7 篇)。
  2. TiDB 发起事务:TiDB 从 PD 的 TSO 服务取一个 start_ts(第 9 篇),以此时间戳读取旧值,构造 Percolator 式 prewrite 请求(第 10 篇)。
  3. TiKV 处理 prewrite:请求打到该 Key 所在 Region 的 Leader,Leader 把这次修改编码成 Raft 日志条目,走 propose → append → commit → apply(第 5 篇),把锁信息写入 CF_LOCK、新值写入 CF_DEFAULT(第 2 篇)。
  4. TiDB 发起 commit:prewrite 全部成功后,TiDB 再取一个 commit_ts,发起 commit 请求,TiKV 同样走一次 propose-apply,把提交记录写入 CF_WRITE,清除 CF_LOCK 里的锁。
  5. TiFlash 异步跟进(可选):如果这张表配置了 TiFlash 副本,Leader 复制给 Follower 的同一条 Raft 日志也会复制给 TiFlash 这个 Learner(第 14 篇),TiFlash 把它转成列存格式,供后续分析查询读取。
sequenceDiagram
  participant App as Application
  participant TiDB
  participant PD
  participant TiKV as TiKV (Region leader)
  participant TiFlash
  App->>TiDB: UPDATE ... WHERE id=42
  TiDB->>PD: get region route (cache miss)
  PD-->>TiDB: region info
  TiDB->>PD: get start_ts
  TiDB->>TiKV: prewrite(key, lock)
  TiKV->>TiKV: propose -> append -> commit -> apply
  TiKV-->>TiDB: prewrite ok
  TiDB->>PD: get commit_ts
  TiDB->>TiKV: commit(key)
  TiKV->>TiKV: propose -> append -> commit -> apply
  TiKV-->>TiDB: commit ok
  TiKV-->>TiFlash: replicate raft log (learner)
  TiDB-->>App: success

这条路径里,每一步都对应本系列后面某一篇要拆开讲的机制:Region 路由对应第 3、7 篇;TSO 对应第 9 篇;prewrite/commit 对应第 10–11 篇;propose 到 apply 对应第 5 篇;TiFlash Learner 对应第 14 篇。读者如果只想搞清楚「一次更新为什么慢」,可以按这条路径逐段排查,而不必通读全部 18 篇。


四、本系列 18 篇地图

系列按「本地引擎 → 写路径与副本 → PD/时间戳 → 事务 → 计算下推与 SQL 边界 → TiFlash 与对照 → 排障与选型」组织,完整篇目依赖关系见 系列 index

flowchart TD
  A["01 Overview"] --> B["02 Key MVCC"]
  A --> C["03 Region model"]
  C --> D["04 Multi-Raft"]
  D --> E["05 raftstore write"]
  E --> F["06 Snapshot log GC"]
  C --> G["07 PD scheduling"]
  G --> H["08 Split hotspot"]
  G --> I["09 TSO"]
  B --> J["10 Percolator"]
  I --> J
  J --> K["11 Pessimistic ResolveLock"]
  E --> L["12 Coprocessor"]
  L --> M["13 TiDB SQL boundary"]
  F --> N["14 TiFlash Learner"]
  N --> O["15 Freshness"]
  D --> P["16 Cockroach compare"]
  K --> Q["17 Troubleshooting"]
  H --> Q
  O --> R["18 Selection"]
  P --> R
  Q --> R

本篇之后紧接的第 2–6 篇是「地基」部分:先讲一条 Key 怎么编码(第 2 篇)、Key 空间怎么切成 Region(第 3 篇)、Region 怎么用 Multi-Raft 复制(第 4 篇)、一次写请求怎么走完 raftstore 的 propose 到 apply(第 5 篇)、副本怎么用 snapshot 追赶和用 log GC 控制磁盘占用(第 6 篇)。这五篇建立的坐标系,是后面 PD 调度、事务、TiFlash 讨论的前提。


五、小结

三句话小结

  1. TiDB、TiKV、PD、TiFlash 各管一层:SQL 翻译、Region/Raft 存储、元数据调度、列存分析副本;出问题时先定位在哪一层,而不是笼统地怀疑「数据库慢」。
  2. TiKV 的核心复杂度是 Region 切分与 Multi-Raft 复制,不是 RocksDB 本身;PD 的核心复杂度是持续调度决策,不是简单的配置存储。
  3. 本系列的落点是 Huang et al.(PVLDB 2020)提出的「同一份 Raft 日志同时喂给行存与列存状态机」这条 HTAP 路线的工程实现,从第 2 篇开始逐层拆开。

下一篇进入本地存储层:Key 编码与 MVCC:三 CF 与时间戳


参考资料

核心论文

  1. Huang, D. et al., TiDB: A Raft-based HTAP Database, PVLDB 13(12): 3072–3084, 2020, DOI: 10.14778/3415478.3415535(多 Raft 存储、Learner 列存、CH-benCHmark 实验)。

规范 / 文档

  1. TiKV Documentation, TiKV in 5 MinutesArchitecture(TiKV 7.x/8.x)。
  2. PD Documentation, Scheduling(PD 7.x)。

站内

  1. RocksDB 内核
  2. Raft 解读
  3. db-frontier/12 HTAP 新范式
  4. distributed/39 分布式 KV 存储对比
  5. 本系列 indexPLAN.md

下一篇Key 编码与 MVCC:三 CF 与时间戳

返回 系列目录 · 数据库索引

同主题继续阅读

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

2026-07-16 · database / storage

【TiKV / HTAP 内核】PD 元数据与调度:心跳、算子与调度器

拆解 PD 如何靠 store/region 心跳建立集群视图,用 balance-leader/balance-region/hot-region 等调度器算分,生成 transfer-leader、move-peer 等 Operator,再通过下一次心跳的响应把算子'建议'给 Region Leader;对照 Spanner placement driver 与 CockroachDB 去中心化再平衡的架构分歧。


By .