TiDB 是 PingCAP 开源的分布式 NewSQL 数据库,兼容 MySQL 协议,具备水平扩展、强一致性、高可用等特性。其架构深受 Google Spanner/F1 启发,融分布式存储、调度、计算于一体。本文深入解析 TiDB 各核心组件的设计原理。
1. TiDB 整体架构
┌─────────────────┐
│ Client/App │
│ (MySQL 协议) │
└────────┬────────┘
│
┌──────────────────┼──────────────────┐
│ │ │
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ TiDB SQL │ │ TiDB SQL │ │ TiDB SQL │
│ Server │ │ Server │ │ Server │
│ (计算层) │ │ (计算层) │ │ (计算层) │
└────┬─────┘ └────┬─────┘ └────┬─────┘
│ │ │
└─────────────────┼──────────────────┘
│
┌───────────────┼───────────────┐
│ │ │
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ PD │ │ PD │ │ PD │
│ (元数据) │ │ (Leader) │ │ (Follower)│
│ 调度中心 │ │ │ │ │
└────┬─────┘ └──────────┘ └──────────┘
│
▼
┌──────────────────────────────────────────────┐
│ TiKV 集群 │
│ Region Region Region Region │
│ ┌───┐ ┌───┐ ┌───┐ ┌───┐ │
│ │A,B│ │C,D│ │E,F│ │G,H│ ... │
│ └───┘ └───┘ └───┘ └───┘ │
│ 每个 Region 默认 96MB,多副本 (Raft) │
└──────────────────────────────────────────────┘
1.1 三个核心角色
| 组件 | 职责 | 技术栈 |
|---|---|---|
| TiDB Server | 接收 SQL → 解析 → 优化 → 执行计划 → 向 TiKV 下推计算 | Go |
| Placement Driver (PD) | 元数据管理(TSO 全局时钟、Region 路由)、集群调度 | Go + etcd |
| TiKV | 分布式键值存储,数据持久化,多副本一致性 | Rust + RocksDB |
2. TiKV:分布式键值存储
2.1 RocksDB 单机引擎
TiKV 使用 RocksDB(Facebook 基于 LevelDB 开发)作为单机存储引擎:
RocksDB 核心结构(LSM-Tree):
MemTable (内存) SSTable (磁盘, L0~L6)
┌─────────┐ ┌───┐
│ Write │ │L0 │ (刚从 MemTable flush,可能有重叠)
│ ▼ │ ├───┤
│ MemTable│ ──►────│L1 │ (每一层大小是上一层的 10 倍)
│ ▼ │ ├───┤
│ WAL │ │L2 │
└─────────┘ └───┘
写入:追加写 WAL → 写 MemTable → 异步刷 SST
读取:MemTable → BlockCache → SST(逐层 BloomFilter 过滤)
优势:写入顺序 IO 极快,适合写密集型场景。
代价:读取可能需要合并多层数据,后台 Compaction 有 IO 放大。
2.2 Region:数据分片单元
TiKV 将数据按 Key 范围划分为 Region,默认大小约 96MB:
Key 空间(全部数据按 Key 排序):
[空]─────[Region 1]─────[Region 2]─────[Region 3]─────[Max]
| k1~k1000 | | k1001~k3000 | | k3001~k5000 |
Leader@A Leader@B Leader@C
Follower@B,C Follower@A,C Follower@A,B
Region 特征:
- 每个 Region 有多个副本(默认 3 副本,通过 Raft 同步)
- Region 可分裂(数据量大时)和合并(数据量小时)
- 副本可在节点间调度(负载均衡、故障恢复)
2.3 Raft 共识协议
TiKV 使用 Raft 保证多副本一致性:
Raft 角色:
┌─────────────────────────────────────────────────────────────┐
│ Leader (A) Follower (B) Follower (C) │
│ │ │ │ │
│ 接收客户端写请求 接收 Leader 接收 Leader │
│ 复制日志到 Follower 心跳/日志复制 心跳/日志复制 │
│ 多数回复后提交 持久化日志 持久化日志 │
└─────────────────────────────────────────────────────────────┘
写流程:
1. Leader 接收写请求
2. Leader 将日志 append 到本地
3. Leader 并行发送 AppendEntries RPC 给所有 Follower
4. 收到 majority(2/3)确认后,日志标记为 committed
5. 返回客户端成功
6. Leader/Follower 异步 apply 到 RocksDB 状态机
2.4 Multi-Raft
TiKV 的每个 Region 是一个独立的 Raft 组,整个集群存在大量 Raft 组并行:
Node A (Store): Region-1(Leader), Region-2(Follower), Region-5(Leader)...
Node B (Store): Region-1(Follower), Region-2(Leader), Region-3(Follower)...
Node C (Store): Region-1(Follower), Region-3(Leader), Region-4(Leader)...
每个 Store 有 ~leader 数均衡 → 写负载均匀分散
3. Placement Driver (PD)
PD 是整个集群的"大脑",负责关键的全局协调工作。
3.1 TSO(Timestamp Oracle)全局时钟
PD 提供全局单调递增时间戳:
TSO = (physical_time << 18) | logical_counter
42 bits 18 bits
物理时间:毫秒级 UNIX 时间戳
逻辑计数:同一毫秒内可分配 262144 个时间戳
TSO 获取:
TiDB Server ──► 批量向 PD 请求时间戳(减少 RPC)
PD Leader 分配并持久化到 etcd
TSO 用于:事务时间戳、MVCC 版本号、数据排序。
3.2 Region 路由
TiDB 需要知道某条数据在哪个 TiKV Region:
Region 路由表(缓存在 TiDB + 存储在 PD):
┌──────────────────┬──────────────────────┬────────────┐
│ Key Range Start │ Key Range End │ Leader │
├──────────────────┼──────────────────────┼────────────┤
│ "" │ "user_1000" │ Store-1 │
│ "user_1000" │ "user_5000" │ Store-2 │
│ "user_5000" │ "order_0000" │ Store-3 │
│ "order_0000" │ "order_9999" │ Store-1 │
└──────────────────┴──────────────────────┴────────────┘
TiDB 缓存 Region 信息,访问时若 Region 已迁移,TiKV 返回错误,TiDB 重新向 PD 获取。
3.3 调度策略
PD 使用心跳从 TiKV 收集信息,执行调度:
| 调度类型 | 触发条件 | 动作 |
|---|---|---|
| Balance Leader | Store 间 Leader 数不均衡 | 转移 Leader |
| Balance Region | Store 间数据量不均 | 迁移 Region Follower |
| Hot Region | 某些 Region 热点访问 | 分裂热点 Region、迁移副本 |
| Store Limit | 新增/下线节点 | 限制迁移速度,避免影响业务 |
| Merge Region | 相邻 Region 都很小 | 合并减少元数据开销 |
4. TiDB SQL 层
4.1 分布式执行引擎
SQL: SELECT region, SUM(amount) FROM orders GROUP BY region
执行计划(分布式聚合):
TiDB (Coordinator)
│
├─► TiKV Node A: Partial SUM(amount) GROUP BY region ──┐
├─► TiKV Node B: Partial SUM(amount) GROUP BY region ──┤
└─► TiKV Node C: Partial SUM(amount) GROUP BY region ──┤
│
◄────────────────────────────────────────────────────────┘
汇总各节点局部结果,计算最终聚合
优化:将聚合下推到 TiKV(Coprocessor),减少网络传输。
4.2 Coprocessor 下推
TiDB 将可以下推的计算发送给 TiKV:
-- 可下推:过滤、聚合、TopN、Limit
SELECT region, COUNT(*) FROM orders
WHERE created_at > '2024-01-01'
GROUP BY region;
-- 不可下推:复杂表达式、子查询(部分)、跨 Region JOIN
SELECT * FROM orders o
WHERE o.amount > (SELECT AVG(amount) FROM orders);
4.3 TiSpark 与 Flink 集成
| 工具 | 用途 | 场景 |
|---|---|---|
| TiSpark | Spark 直接读取 TiKV | 大规模 ETL、数据科学 |
| TiDB Lightning | 快速数据导入 | 初始数据迁移 |
| Dumpling | 逻辑备份导出 | 兼容性备份 |
| TiCDC | 实时变更同步 | CDC 到 Kafka/MySQL |
5. 事务模型
5.1 乐观事务(默认)
乐观事务流程:
BEGIN
│──► 所有读取从缓存(不检查锁)
│──► 所有写入缓存在客户端
│
COMMIT
│──► TiDB 向 TiKV 发起两阶段提交(2PC)
│ ├── Prewrite 阶段:加锁、写数据
│ └── Commit 阶段:提交、释放锁
│──► 如果冲突(锁被其他事务持有)→ 回滚,返回 Write Conflict 错误
乐观事务假设冲突很少,提交时检测冲突。
5.2 悲观事务
悲观事务流程(BEGIN PESSIMISTIC):
BEGIN PESSIMISTIC
│──► SELECT FOR UPDATE → 向 TiKV 加悲观锁
│──► UPDATE/INSERT → 加悲观锁
│
COMMIT
│──► 2PC 提交
└── Prewrite 已加锁,冲突概率大幅降低
-- 开启悲观事务
SET @@tidb_txn_mode = 'pessimistic';
BEGIN PESSIMISTIC;
SELECT * FROM inventory WHERE product_id = 1 FOR UPDATE; -- 加悲观锁
UPDATE inventory SET count = count - 1 WHERE product_id = 1;
COMMIT;
5.3 事务对比
| 维度 | 乐观事务 | 悲观事务 |
|---|---|---|
| 锁策略 | 提交时加锁 | 语句执行时加锁 |
| 冲突处理 | 重试/报错 | 阻塞等待 |
| 适用场景 | 低冲突、写少读多 | 高冲突、并发修改 |
| 延迟 | 低(无阻塞) | 有锁等待延迟 |
| MySQL 兼容 | 需应用处理冲突 | 兼容度更高 |
6. TiFlash 列存引擎
TiFlash 是 TiKV 的 Raft Learner:
Raft Log
│
TiKV Leader ───► TiKV Follower
│ │
│ │
└──► Raft Learner ──► TiFlash (异步 apply,列存格式)
(不投票,不影响 TiKV 写入性能)
延迟:通常 < 1 秒(Raft Log 复制延迟)
TiFlash 使用 Delta Tree 存储引擎,支持向量化执行和 MPP 查询,与 TiDB 优化器协同实现 HTAP。
7. 运维关键命令
# 集群状态
tiup cluster display tidb-cluster
# 查看 Region 分布
pd-ctl region
# 查看热点
pd-ctl hot read
pd-ctl hot write
# TiDB 执行计划
EXPLAIN ANALYZE SELECT ...;
# 查看慢查询
SELECT * FROM information_schema.SLOW_QUERY LIMIT 10;
# 数据导出(Dumpling)
dumpling -h 127.0.0.1 -P 4000 -u root -o /tmp/export
# 数据导入(Lightning)
tidb-lightning --config lightning.toml
8. 总结
TiDB 架构的核心设计思想:
1. 计算与存储分离
├── TiDB (无状态计算) 可独立扩容
└── TiKV (有状态存储) 通过 Region + Raft 水平扩展
2. 一致性保证
└── Multi-Raft 实现分片级强一致性
3. 全局协调
└── PD 提供 TSO 时钟、Region 路由、智能调度
4. HTAP 统一
└── TiKV 行存 + TiFlash 列存,优化器自动选择
5. MySQL 兼容
└── 协议/语法兼容,降低迁移成本
TiDB 不是银药,其分布式事务延迟(网络往返)高于单机 MySQL,适合:
- 数据量 > 1TB 或 QPS > 10000 的场景
- 需要水平扩展而无暇分库分表
- 需要 HTAP 统一架构
对于中小规模应用,单机 MySQL/PostgreSQL + 读写分离仍然是性价比最高的选择。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。