跳转到内容

TiDB 2020 — 给 Raft 加一个"旁听生",让一份数据同时跑事务和分析

是什么

TiDB 是一个让你只用一个数据库,就能既跑业务交易、又跑数据分析的分布式系统。它的核心招式叫 HTAP(Hybrid Transactional and Analytical Processing,混合事务+分析处理)。

日常类比:以前公司有”白天柜台”和”晚上财务”两个系统——白天柜台收钱(事务),晚上把数据导到财务系统跑报表(分析)。问题是报表看到的总是”昨天的数据”。TiDB 的做法相当于:在柜台旁边坐一个旁听员,柜台每写一笔单据他就抄一份成”报表友好格式”,财务直接读他这份,不用等导出。

技术层面这个旁听员叫 Raft learner(学习者节点):不投票、不参与多数派、单纯异步复制日志。柜台叫 TiKV(行存),旁听员叫 TiFlash(列存),调度官叫 PD(Placement Driver)。

-- 用户视角和 MySQL 一模一样
SELECT count(*) FROM orders WHERE city = 'shanghai';
-- 优化器自动决定走 TiKV(行存索引)还是 TiFlash(列存全扫)

为什么重要

不理解 TiDB 这套设计,下面这些事都说不清:

  • 为什么”实时分析”在 2010 之前都做不到——纯靠 ETL 同步至少滞后几小时
  • 为什么 Snowflake / PolarDB / 后来很多 HTAP 系统都抄了”日志异步出列存副本”思路
  • 为什么 NewSQL(CockroachDB / Spanner)和 HTAP 不是一回事——前者只解决 OLTP 扩展性
  • 为什么 PingCAP 这家中国创业公司能把 VLDB 论文写成产品落地,而不是相反

核心要点

TiDB 把 HTAP 拆成三个工程问题,每个都有专门答案:

  1. 新鲜度(freshness)——分析要看到刚写的数据。靠 Raft learner 把日志异步拉到列存,延迟 < 100ms(论文实测),不是几小时的 ETL。类比:旁听员就坐在柜台旁,写完立刻抄。

  2. 隔离性(isolation)——分析查询不能拖慢交易。把列存(TiFlash)部署在独立机器上,CPU/内存/磁盘都和行存(TiKV)分开。类比:旁听员有自己的桌子,柜台不会被他翻账本翻慢。

  3. 一致性(consistency)——读列存和读行存得到同一份事实。靠 learner 强一致协议:读 TiFlash 时先去问 leader 当前 commit index,等本地 apply 到这个点再返回。

三件事合起来构成 “Raft learner = 把共识算法从 ‘只为高可用’ 升级成 ‘高可用 + HTAP 副本’” 这一篇论文的全部贡献。

实践案例

案例 1:MySQL 协议接进来,自动分片

-- 客户端连 TiDB 就像连 MySQL
mysql -h tidb-server -P 4000 -u root
> CREATE TABLE orders (id BIGINT PRIMARY KEY, city VARCHAR(20), amount INT);
> INSERT INTO orders VALUES (1, 'shanghai', 100);
> SELECT * FROM orders WHERE id = 1;

逐部分解释

  • 数据按主键 id 范围切成多个 Region(默认每个 96MB),分布在不同 TiKV 节点
  • PD 自动决定哪个 Region 放哪台机器,hot Region 还会主动 split / 迁移
  • 用户完全感觉不到分片,写一条 SQL 就行,这是 NewSQL 的核心承诺

案例 2:Raft learner 异步同步行转列

[Leader (TiKV 行存)]
写入 row: {id=1, city=shanghai, amount=100}
→ Raft log entry 写入
→ 同步到 follower(多数派提交)→ 用户拿到 ack
→ 异步推给 learner (TiFlash)
收到 log,转成列格式:
id 列: [1, ...]
city 列: [shanghai, ...]
amount 列:[100, ...]

关键点:learner 计入 quorum,所以它慢了不会拖累事务延迟;但 leader 会持续推日志给它,平均 < 100ms 后列存就有这条数据。

案例 3:优化器同时用行存和列存

SELECT T.*, S.a FROM T JOIN S ON T.b = S.b WHERE T.a BETWEEN 1 AND 100;

TiDB CBO(基于代价的优化器)会算三条路径的成本:

  • T 走 TiKV 索引扫(因为 a BETWEEN 1 AND 100 范围窄,命中行少)
  • S 走 TiFlash 列扫(因为只要两列 a / b,列存压缩比高、扫得快)
  • 最后在 SQL 层做 hash join

这就是论文最妙的地方:因为两边数据一致,优化器可以混搭行存和列存路径,而不是非此即彼。

踩过的坑

  1. 列存延迟不是 0:learner 异步拉日志再做行转列,重写入压力下能到 100-300ms 滞后。如果业务要”刚写完立刻在 BI 报表里看到”,要在 SQL 里强制 SET tidb_read_staleness=0 等同步,会把延迟暴露给查询。

  2. Region 太热 split 不动:默认 96MB 一个 Region,热点 key(比如自增 ID 永远写最新那个 Region)全压在一个 Raft leader 上。要么改 schema 用 hash 前缀打散,要么靠 PD 提前 pre-split,否则单 leader 写入瓶颈。

  3. PD 是单点逻辑(虽然多副本):所有事务都要找 PD 拿 timestamp(时间戳),PD 集群挂了整个数据库停写。生产部署必须 3 节点 PD,且不能离 SQL 层网络太远——每次事务一次 RPC 累计起来不便宜。

  4. 跨 Region 大事务慢:Percolator 风格 2PC(两阶段提交)跨多个 Region 时要做两轮 RPC(prewrite + commit),写入键多了延迟成倍涨。OLTP 设计上要避免一个事务横跨太多 Region。

适用 vs 不适用场景

适用

  • 业务既要做事务(订单、库存)又要看实时报表(销售看板、风控)——一份数据避免 ETL
  • 数据量超过单机 MySQL 上限(>1TB),又不想拆库拆表手动管
  • 需要 MySQL 协议兼容(业务代码不重写)+ 水平扩展能力
  • 报表延迟从 “T+1” 降到 “秒级”,对业务有真实价值

不适用

  • 纯 OLTP 极限延迟(<1ms)→ 选 aurora / 单机 MySQL,TiDB 走 Raft 多数派固有 ms 级延迟
  • 纯 OLAP 大查询批跑 → 选 bigtable-2006 / Snowflake,TiDB 列存为”鲜度优先”不是”扫描极致”
  • 跨数据中心强一致 → 选 spanner-2012(TrueTime),TiDB 跨 DC 部署延迟翻倍
  • 强一致全局快照(多区一次性看同一时刻)→ Spanner / calvin-2012 更专门

历史小故事(可跳过)

  • 2015 年:PingCAP 在北京创立,目标做开源 NewSQL;早期只有 TiKV(用 Rust 写、靠 Raft 复制),还没列存
  • 2017 年:TiDB 1.0 GA,主打 “MySQL 协议 + 水平扩展”,但分析查询慢得没法用
  • 2018-2019 年:试过 “TiSpark 直连 TiKV 跑分析”,行存扫起来仍然慢;意识到必须有列存
  • 2020 年:这篇 VLDB 论文公开 TiFlash + Raft learner 方案,这是中国 NewSQL 第一篇上 VLDB 的工业论文
  • 之后:Snowflake 的 Hybrid Tables、PolarDB-IMCI、Yugabyte 都借鉴 “log-based 异步列存副本” 思路

学到什么

  1. 共识算法不只是为了高可用——Raft 多副本本来是防故障,TiDB 把它升级成”防故障 + 出 HTAP 副本”,思路迁移性极强
  2. 异步 ≠ 不一致——learner 拉日志虽然异步,但读时强制等 apply,外部观察到的仍是强一致
  3. NewSQL → HTAP 是自然演化——任何已经在做共识复制的系统都可以走这条路,不用从头造
  4. 代价模型决定一切——同一份数据有行存和列存两条路,优化器代价模型必须能选对,否则架构白搭

延伸阅读

关联

  • raft —— TiDB 的复制协议基础,learner 是它加的角色
  • paxos-1998 —— 比 Raft 老一辈的共识算法,理论上等价但难实现
  • spanner-2012 —— 同代竞品,靠 TrueTime 做跨 DC 强一致;TiDB 选了”单 DC HTAP”路线
  • cockroachdb-2020 —— 同期 NewSQL,纯 OLTP 路线,论文做过对比
  • f1-2013 —— Spanner 上的 SQL 引擎,是 TiDB SQL 层灵感来源之一
  • rocksdb-lsm —— TiKV 底层存储引擎
  • bigtable-2006 —— TiKV 的 key-value 模型源头

反向链接

  • aurora —— Aurora — 把数据库的下半身换成日志机
  • bigtable-2006 —— Bigtable 2006 — Google 把行级随机读写做到 PB 级的存储系统
  • calvin-2012 —— Calvin 2012 — 先排好顺序再执行,让跨分区事务不再走 2PC
  • cockroachdb-2020 —— CockroachDB 2020 — 没原子钟也能做全球强一致 SQL 数据库
  • paxos-1998 —— Paxos 1998 — 古希腊议会寓言里藏的共识协议
  • raft —— Raft — 易理解的共识算法
  • rocksdb-lsm —— LSM-tree 与 RocksDB — 把所有写都变成顺序写
  • spanner-2012 —— Spanner 2012 — 用原子钟和 GPS 给全球数据库发时间戳
  • vitess —— Vitess — 给 MySQL 装上水平分片的代理层