基于 TiDB 官方 v8.x / v7.1.x
目录
- 一、什么是分布式事务
- 二、核心架构
- 三、技术基石:Percolator 模型
- 四、两阶段提交(2PC)详解
- 五、乐观事务 vs 悲观事务
- 六、事务隔离级别
- 七、ACID 保证
- 八、垃圾回收(GC)
- 九、XA 事务兼容性
- 十、最佳实践与常见问题
- 十一、演进时间线
一、什么是分布式事务
分布式事务是指在分布式数据库中,事务的参与者、资源管理器、事务管理器分布在不同的物理节点上,它们需要通过网络通信协同工作,共同保证事务的 ACID 特性。
核心挑战
| 挑战 | 说明 |
|---|---|
| 网络不可靠 | 节点间通信可能延迟、丢包、乱序 |
| 节点可能故障 | 任一参与节点可能在事务执行中宕机 |
| 时钟不一致 | 不同节点难以精确同步时钟 |
| 数据跨节点分布 | 一个事务涉及的数据可能分布在多个物理节点上 |
TiDB 的解决思路
"对用户完全透明"——开发者和使用单机 MySQL 一样写 BEGIN / COMMIT / ROLLBACK,底层自动处理跨节点的数据一致性。
-- 和单机 MySQL 完全相同的语法
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT; -- TiDB 自动保证跨 Region、跨 TiKV 节点的原子提交
二、核心架构
分布式事务的实现依赖于 TiDB 的三层架构协同工作。
┌──────────────────────────────────────────────────────────┐
│ TiDB Server │
│ (SQL 层) │
│ ┌────────────────────────────────────────────────────┐ │
│ │ • SQL 解析与执行计划生成 │ │
│ │ • 作为 2PC 协调者(Coordinator) │ │
│ │ • 与 PD 交互获取 TSO 时间戳 │ │
│ │ • 缓存写入,commit 时统一提交 │ │
│ └────────────────────────────────────────────────────┘ │
└────────────────────────┬─────────────────────────────────┘
│ gRPC 通信
┌────────────────────────▼─────────────────────────────────┐
│ PD — Placement Driver │
│ ┌────────────────────────────────────────────────────┐ │
│ │ • 全局授时服务(TSO — Timestamp Oracle) │ │
│ │ • 管理 Region 分布与元数据 │ │
│ │ • 执行调度策略(Leader 均衡 / Region 均衡) │ │
│ └────────────────────────────────────────────────────┘ │
└────────────────────────┬─────────────────────────────────┘
│
┌────────────────────────▼─────────────────────────────────┐
│ TiKV (存储层) │
│ ┌────────────────────────────────────────────────────┐ │
│ │ • 分布式 KV 存储,数据按 Range 切分为 Region │ │
│ │ • 每个 Region 使用 Raft 维护 3 副本 │ │
│ │ • 每个 Region 有一个 Leader 负责读写 │ │
│ │ • 执行 prewrite / commit / pessimistic lock 等操作 │ │
│ │ • 使用 RocksDB 进行本地持久化 │ │
│ └────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────┘
各层的事务职责
| 组件 | 事务中的角色 |
|---|---|
| TiDB Server | 2PC 协调者(Coordinator):获取 TSO、执行 prewrite 和 commit、处理冲突与重试 |
| PD | 全局授时服务:分配全局唯一单调递增的时间戳(TSO) |
| TiKV | 事务参与者(Participant):执行具体的数据写入、锁管理、MVCC 版本控制 |
三、技术基石:Percolator 模型
TiDB 的事务模型源自 Google Percolator(Google 在 BigTable 之上实现的事务系统),并做了大量工程优化。
核心思想
在已有的分布式 KV 存储之上增加事务层,提供跨行、跨表的事务能力。
四大机制
┌──────────────────────────────────────────────────────────┐
│ Percolator 事务模型 │
│ │
│ TSO 全局时钟 ←─── 为每个操作分配全局唯一时间戳 │
│ │ │
│ ▼ │
│ MVCC 多版本 ←─── 每个 Key 保存多个版本历史 │
│ │ │
│ ▼ │
│ Primary Key 机制 ←─── 事务状态以 Primary 为准 │
│ │ │
│ ▼ │
│ 两阶段提交 2PC ←─── Prewrite + Commit 两阶段 │
│ │
└──────────────────────────────────────────────────────────┘
TSO(Timestamp Oracle)全局时间戳
PD 提供一个全局唯一、单调递增的时间戳服务。
-- 查看当前事务的 start_ts
BEGIN;
SET @ts := @@tidb_current_ts;
SELECT @ts;
-- 返回示例:443852055297916932
ROLLBACK;
TSO 的组成:
TSO = 物理时间(毫秒) + 逻辑时间(同毫秒内计数器)
└───── 高 46 位 ────┘ └───── 低 18 位 ─────┘
- 开启事务时,TiDB 从 PD 获取 TSO 作为
start_ts - 提交时获取
commit_ts - 所有读写基于这些时间戳,实现快照隔离
解码 TSO:
-- TiDB 内置函数
SELECT TIDB_PARSE_TSO(443852055297916932);
-- 返回物理时间
SELECT TIDB_PARSE_TSO_LOGICAL(443852055297916932);
-- 返回逻辑计数器
MVCC 多版本并发控制
每个 Key 在 TiKV 中存储多个版本,通过时间戳区分:
Key "A" 在 TiKV 中的存储(逻辑视图):
Key_A @ ts=50 → Value "v3" ← 当前最新版本
Key_A @ ts=30 → Value "v2"
Key_A @ ts=10 → Value "v1"
- 事务读取时以
start_ts为快照点,读取 <=start_ts的最新版本 - 写入时创建新版本,不覆盖旧数据
- 旧版本由 GC 定期清理
Primary Key 机制
每个分布式事务选择一个 Key 作为 Primary Key,它是事务状态的权威标志:
| Primary Key 状态 | 事务整体状态 |
|---|---|
| 已提交(存在 commit 记录) | 已提交 |
| 未提交(只有 prewrite 锁) | 未完成 |
为什么需要 Primary Key?
当 TiDB Coordinator 在 2PC 过程中崩溃,或者某个 TiKV 节点出现锁残留时,其他节点通过检查 Primary Key 的状态就能唯一确定该事务应该提交还是回滚。这就是 Resolve Locks 机制的基础。
四、两阶段提交(2PC)详解
这是 TiDB 分布式事务的核心流程。下面以乐观事务为例,逐步拆解。
整体流程概览
┌─────────────────────────────────────────────────────────────────────┐
│ 事务执行全流程 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ BEGIN │───▶│ 执行 SQL │───▶│ 2PC Phase1│───▶│ 2PC Phase2│ │
│ │获取TSO │ │ 缓存写入 │ │ Prewrite │ │ Commit │ │
│ └────────┘ └──────────┘ └──────────┘ └──────────┘ │
│ │ │ │
│ ┌─────────────┘ │ │
│ ▼ ▼ │
│ start_ts 写入数据 commit_ts 确认提交 │
│ │
└─────────────────────────────────────────────────────────────────────┘
阶段一:Prewrite(预写)
TiDB (Coordinator) TiKV (Participants)
│ │
│ ① 选择一个 Key 作为 Primary Key │
│ ② 按 Region 分组所有待写入 Key │
│ │
│ ③ prewrite(primary_key, start_ts) │
├─────────────────────────────────────►│ 先写入 Primary
│◄─────────────────────────────────────│ 成功
│ │
│ ④ prewrite(keys, primary, start_ts) │
├─────────────────────────────────────►│ 并发写入所有 Secondary
├─────────────────────────────────────►│
├─────────────────────────────────────►│
│◄─────────────────────────────────────│ 全部成功
│ │
│ ✓ 所有 prewrite 成功,进入阶段二 │
Prewrite 检查什么?
TiKV 在 prewrite 时会检查目标 Key:
- 是否存在其他事务的锁(Lock)—— 有则等待或冲突
- 是否有更新的已提交数据(
data.commit_ts > txn.start_ts)—— 有则返回 Write Conflict - 验证 Schema 版本是否一致
阶段二:Commit(提交)
│ │
│ ⑤ 从 PD 获取 commit_ts │
│ ──── PD ──────────────────────────► │
│ │
│ ⑥ commit(primary_key, commit_ts) │
├─────────────────────────────────────►│ 先提交 Primary
│◄─────────────────────────────────────│ 成功!
│ │
│ ⭐ 返回客户端 "SUCCESS" │
│ ──── 外部可见 ────────────────────► │
│ │
│ ⑦ commit(secondary_keys, commit_ts) │
├─────────────────────────────────────►│ 异步并发 Secondary
├─────────────────────────────────────►│
├─────────────────────────────────────►│
关键设计要点:
| 要点 | 说明 |
|---|---|
| 先提交 Primary | Primary Key 的成功提交即代表整个事务提交。这是事务原子性的核心保证 |
| 先返回成功,再提交 Secondary | 一旦 Primary 提交成功,立即返回客户端成功,Secondary 异步提交 |
| 异步容错 | Secondary 即使提交失败,后续 GC 或 Resolve Locks 也会根据 Primary 的状态补全 |
| 并发执行 | Prewrite 和 Commit 阶段都按 Region 分组后并发执行 |
为什么先提交 Primary Key?
这是 Percolator 模型的精妙设计:
场景:Coordinator 在提交 Primary 成功、但 Secondary 还未全部提交时崩溃
恢复后:
TiKV-1(Primary已提交) → "事务已提交,继续提交 Secondary"
TiKV-2(Secondary尚在prewrite) → "检查 Primary 状态 → 已提交 → 补提交"
TiKV-3(Secondary未响应) → "检查 Primary 状态 → 已提交 → 补提交"
最终:所有 Secondary 都被补全提交,原子性得到保证。
相反,如果 Secondary 都提交了但 Primary 未提交,其他节点通过检查 Primary 会判定事务未完成,从而回滚所有 Secondary —— Primary 永远是正确的答案。
完整时序图
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│ Client │ │ TiDB │ │ PD │ │ TiKV │
└────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘
│ │ │ │
│ BEGIN │ │ │
├──────────────►│ get start_ts │ │
│ ├──────────────►│ │
│ │◄──────────────┤ │
│ │ │ │
│ SQL 执行阶段 │ │ │
├──────────────►│ │ │
│ (读写操作) │ get/lock │ │
│ ├──────────────────────────────►│
│ │◄──────────────────────────────┤
│◄──────────────┤ │ │
│ │ │ │
│ COMMIT │ │ │
├──────────────►│ │ │
│ │ │ │
│ ── 2PC 开始 ── │ │
│ │ 选择 Primary │ │
│ │ 按Region分组 │ │
│ │ │ │
│ ── Phase 1: Prewrite ── │ │
│ │ prewrite(PK) │ │
│ ├──────────────────────────────►│
│ │◄──────────────────────────────┤
│ │ prewrite(SKs) │ │
│ ├──────────────────────────────►│
│ ├──────────────────────────────►│(并发)
│ │◄──────────────────────────────┤
│ │ │ │
│ ── Phase 2: Commit ── │ │
│ │ get commit_ts │ │
│ ├──────────────►│ │
│ │◄──────────────┤ │
│ │ │ │
│ │ commit(PK) │ │
│ ├──────────────────────────────►│
│◄══════════════┤════ 成功 ════ │ │ ← 此时返回
│ │ │ │
│ │ commit(SKs) │ │ ← 异步提交
│ ├──────────────────────────────►│
│ ├──────────────────────────────►│(并发)
│ │ │ │
1PC / Async Commit 优化
v4.0+ 引入了 1PC 和 Async Commit 优化:
| 模式 | 说明 | 适用场景 |
|---|---|---|
| 2PC | 标准的两阶段提交(prewrite + commit) | 跨 Region 的通用写入 |
| 1PC | 只需一次 RPC,跳过 prewrite 阶段直接提交 | 单 Region 事务 |
| Async Commit | Prewrite 后不等待所有 Secondary 确认就返回 | 延迟敏感的业务 |
TiDB 会根据事务涉及的 Region 数量自动选择合适的提交路径。
五、乐观事务 vs 悲观事务
TiDB 从 v3.0.8 起同时支持两种事务模式,这是 TiDB 事务体系的重要演进。
对比总览
| 对比维度 | 乐观事务(Optimistic) | 悲观事务(Pessimistic) |
|---|---|---|
| 加锁时机 | 仅在 commit 时的 prewrite 阶段检查冲突 | DML 执行时就对目标行加悲观锁 |
| 冲突处理 | 提交时检测到冲突 → Write Conflict 错误 → 整个事务重试 |
写入即阻塞等待,类似 MySQL InnoDB |
| 重试成本 | 高(事务整体重试,需要清理旧锁) | 低(只需等待锁释放) |
| 适用场景 | 低冲突、批量写入、分析型负载 | 高并发 OLTP、秒杀、热点更新 |
| 锁开销 | 几乎为零(无锁) | 有锁维护成本 |
| 死锁检测 | 不存在死锁 | 支持死锁检测(Error 1213) |
| 默认模式 | v3.0.8 之前默认 | v3.0.8 起推荐默认 |
乐观事务流程
BEGIN (获取 start_ts)
│
▼
SQL 执行(全部缓存到内存,不与 TiKV 交互)
│
▼
COMMIT(触发 2PC)
├─ Prewrite → 检查冲突
│ ├─ 无冲突 → Commit → 成功
│ └─ 有冲突 → Write Conflict Error
│ → 自动获取新 start_ts 重试(如果开启)
└─ 重试限制:tidb_retry_limit(默认 10 次)
Write Conflict 日志示例:
[WARN] [session.go:446] ["commit failed"]
[error="[kv:9007]Write conflict,
txnStartTS=416617006551793665,
conflictStartTS=416617018650001409,
conflictCommitTS=416617023093080065,
key={tableID=47, indexID=1, indexValues={string, }}
primary={tableID=47, indexID=1, indexValues={string, }}
[try again later]"]
悲观事务流程
BEGIN (获取 start_ts)
│
▼
DML 执行
├─ 对目标行加悲观锁
│ ├─ 无冲突 → 锁定成功,在内存中缓存修改
│ └─ 有冲突 → 等待锁释放(超时阈值:innodb_lock_wait_timeout)
└─ 加锁成功后继续执行
│
▼
COMMIT
├─ 触发 2PC(Prewrite + Commit)
└─ 释放所有锁
配置方式:
-- 全局切换为悲观事务
SET GLOBAL tidb_txn_mode = 'pessimistic';
-- 单事务指定
BEGIN PESSIMISTIC;
-- 或
BEGIN /*T! PESSIMISTIC */;
-- 查询当前模式
SELECT @@tidb_txn_mode;
Pipelined 悲观锁(v4.0.9+)
TiDB TiKV Leader TiKV Follower
│ │ │
│ Lock(key) │ │
├──────────────►│ 异步 Raft 复制 │
│◄══════════════│══════ 立即返回 ═══►│ ← 不等 Raft 落盘就返回
│ ├───────────────────►│ 异步复制日志
│ ├───────────────────►│
- 效果:获取悲观锁的延迟从 Raft 持久化延迟降低到内存级延迟
- 配置:TiKV 的
[pessimistic-txn] pipelined = true(v4.0.9+ 默认开启)
In-Memory 悲观锁(v6.0.0+)
更进一步,悲观锁直接在 Region Leader 内存中完成,完全不写盘:
- Region Leader 内存中维护锁表
- 只有当内存锁表满时才 fallback 到磁盘模式
- 大幅降低悲观锁的 P99 延迟
如何选择
| 工作负载 | 推荐模式 | 原因 |
|---|---|---|
| OLTP 高并发短事务 | 悲观事务 | 避免 Write Conflict 重试,延迟稳定 |
| 秒杀/热点更新 | 悲观事务 | 保证更新顺序,不会反复重试 |
| 批量 ETL/导入 | 乐观事务 | 减少加锁开销,冲突概率低 |
| 纯分析查询 | 两者均可 | 只读事务无冲突 |
| 不确定的场景 | 悲观事务 | 行为与 MySQL 一致,兼容性最好 |
六、事务隔离级别
默认隔离级别:可重复读(Repeatable Read)
TiDB 的 REPEATABLE READ 基于 Snapshot Isolation(快照隔离) 实现,实际上比 ANSI SQL 标准更强:
| 异常现象 | ANSI RR | MySQL RR | TiDB RR |
|---|---|---|---|
| 脏读(Dirty Read) | ✗ | ✗ | ✗ |
| 不可重复读(Fuzzy Read) | ✗ | ✗ | ✗ |
| 幻读(Phantom) | 可能 ✓ | 可能 ✓ | ✗ 不会发生 |
为什么 TiDB 不会幻读?
因为所有读取都基于事务开始时的 start_ts 快照。无论其他事务何时插入新行,当前事务的快照中"看不到"它们。
读已提交(Read Committed)
-- 设置当前会话
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
- v4.0.0-beta 开始支持
- 每个语句能看到最新的已提交数据
- 行为类似 Oracle / PostgreSQL 的 Read Committed
优化手段:
| 系统变量 | 作用 | 引入版本 |
|---|---|---|
tidb_rc_read_check_ts |
SELECT 语句避免每次请求最新 TSO | v6.0.0 |
tidb_rc_write_check_ts |
减少 DML 写冲突检查次数 | v6.3.0 |
隔离级别矩阵
-- 查看当前隔离级别
SELECT @@transaction_isolation;
-- 设置隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
注意: TiDB 不支持
READ UNCOMMITTED和SERIALIZABLE级别,设置时会降级为REPEATABLE READ或READ COMMITTED。
MySQL 兼容性差异
| 特性 | MySQL InnoDB | TiDB |
|---|---|---|
| Gap Locking(间隙锁) | 支持 | 不支持 |
SELECT LOCK IN SHARE MODE |
支持 | 需开启 tidb_enable_noop_functions |
START TRANSACTION WITH CONSISTENT SNAPSHOT |
支持 | 无实际效果 |
FOR UPDATE NOWAIT |
MySQL 8.0+ | 支持(Error 3572) |
| 死锁检测(Error 1213) | (悲观模式) |
七、ACID 保证
原子性(Atomicity)
保证机制:2PC + Primary Key + Resolve Locks
┌─ Primary Key 已提交 ──▶ 补全提交所有 Secondary
│
残留锁发现 ───┤
│
└─ Primary Key 未提交 ──▶ 回滚所有 Secondary
- Partition 1:2PC 确保所有参与者要么全部 prewrite 成功,要么全部回滚
- Partition 2:Coordinator 崩溃后,其他节点通过 Primary Key 决议
- Partition 3:GC 过程中的 Resolve Locks 阶段自动清理残留事务
一致性(Consistency)
保证机制:SQL 层约束 + KV 层一致性
| 层面 | 保证内容 |
|---|---|
| TiDB SQL 层 | 唯一键约束、外键约束、CHECK 约束检查 |
| TiDB DDL 层 | Online Schema Change,DML 与 DDL 不会冲突导致数据损坏 |
| TiKV 层 | Raft 协议保证所有副本数据最终一致 |
隔离性(Isolation)
保证机制:MVCC + TSO 时间戳
- 每个事务读取自己
start_ts时刻的一致性快照 - 写入通过 2PC 的 prewrite 阶段进行冲突检测
- 悲观模式下通过锁机制保证写写隔离
持久性(Durability)
保证机制:Raft 共识协议
Client → TiKV Leader → 写入 Raft Log
→ 同步到多数派 Follower(至少 2/3)
→ 返回成功给 Client
→ 异步应用到 RocksDB
- 数据写入 必须同步到多数派副本才算成功
- 即使部分 TiKV 节点宕机,数据不丢失
- Raft Log 和实际数据都持久化在 RocksDB 文件中
八、垃圾回收(GC)
GC 是分布式事务的"保洁员",负责清理 MVCC 产生的历史版本。
GC 流程
TiDB GC Leader 驱动(10 分钟一次,默认)
┌─ 1. Resolve Locks(解决残留锁)
│ 扫描 safe point 之前的残留锁
│ 通过检查 Primary Key 状态,提交或回滚过期事务
│
├─ 2. Delete Ranges(删除废弃数据范围)
│ 清理 DROP TABLE / DROP INDEX / TRUNCATE 产生的数据
│
└─ 3. Do GC(清理旧版本)
删除每个 Key 在 safe point 之前的所有历史版本
GC 关键配置
| 系统变量 | 默认值 | 说明 |
|---|---|---|
tidb_gc_enable |
ON | 是否启用 GC |
tidb_gc_run_interval |
10m | GC 运行间隔 |
tidb_gc_life_time |
10m | 历史版本保留时间(安全窗口) |
tidb_gc_concurrency |
自动 | GC 并发度 |
tidb_gc_max_wait_time |
0 | GC 等待 safe point 推进的最长时间 |
-- 查看 GC 配置
SELECT * FROM mysql.tidb WHERE variable_name LIKE 'tikv_gc%';
GC 模式演进
| 版本 | 模式 | 说明 |
|---|---|---|
| v3.0 之前 | CENTRAL GC | TiDB 逐个扫描所有 Region 执行 GC,效率低 |
| v3.0+ | DISTRIBUTED GC | TiKV 每个 Region Leader 自行执行 GC |
| v5.0+ | Compaction Filter GC | RocksDB Compaction 时直接过滤过期数据,零 GC 开销 |
Compaction Filter GC 是最优模式(v5.0+ 默认):
- GC 在 RocksDB 后台 Compaction 过程中顺带完成
- 不需要单独的 GC Worker 扫描和删除
- 对业务几乎零影响
-- 查看当前 TiKV 是否启用 Compaction Filter GC
SHOW CONFIG WHERE type = 'tikv' AND name LIKE '%enable-compaction-filter%';
-- 在线修改
SET CONFIG tikv gc.enable-compaction-filter = true;
九、XA 事务兼容性
TiDB 兼容 MySQL XA 协议,允许作为资源管理器(RM)参与外部分布式事务协调。
适用场景
┌──────────────────┐
│ Atomikos / │
│ Seata / Narayana │ ← 分布式事务协调器(TM)
└────────┬─────────┘
│ XA 协议
┌─────────────┼─────────────┐
│ │ │
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ TiDB │ │ MySQL │ │ Oracle │ ← 多个资源管理器(RM)
└──────────┘ └──────────┘ └──────────┘
配置示例
<!-- Spring + Atomikos 配置 -->
<bean id="xaDataSource" class="com.atomikos.jdbc.AtomikosDataSourceBean">
<property name="uniqueResourceName" value="tidb_ds" />
<property name="xaDataSourceClassName"
value="com.mysql.cj.jdbc.MysqlXADataSource" />
<property name="xaProperties">
<props>
<prop key="URL">jdbc:mysql://tidb:4000/test</prop>
<prop key="user">root</prop>
</props>
</property>
<property name="maxPoolSize" value="10" />
</bean>
重要说明
| 注意点 | 说明 |
|---|---|
| 不同层级 | TiDB 内部的 Percolator 事务与 XA 协议处于不同抽象层 |
| 适用范围 | Percolator 保证跨 Region/跨 TiKV 的一致性;XA 保证跨多个异构数据库 |
| 性能开销 | XA 事务需要额外的协调器交互,延迟高于原生事务 |
十、最佳实践与常见问题
10.1 大事务处理
TiDB 限制:
| 限制项 | 默认上限 | 说明 |
|---|---|---|
| 单事务 KV 条目数 | 3000 行/批(推荐) | 超过可能触发 transaction is too large |
| 单条 KV Entry | 6 MB | 单行数据推荐不超过此值 |
| 事务总大小 | 120 MB(v8.x) | 超过可能被 PD 拒绝 |
分批提交方案:
// 错误示例:一次性提交大量数据
for _, row := range rows {
db.Exec("INSERT INTO t VALUES (?)", row)
}
tx.Commit() // ❌ 可能触发 transaction is too large
// 正确示例:分批提交(推荐每批 3000 行)
const batchSize = 3000
for i := 0; i < len(rows); i += batchSize {
tx, _ := db.Begin()
end := i + batchSize
if end > len(rows) {
end = len(rows)
}
for _, row := range rows[i:end] {
tx.Exec("INSERT INTO t VALUES (?)", row)
}
tx.Commit() // 每批单独提交
}
10.2 监控事务性能
Grafana → TiDB Dashboard
关键面板:
⚡ KV Errors → KV Backoff OPS ← 事务冲突/重试频率
⚡ KV Errors → Lock Resolve OPS ← 锁解析频率(正常应为低值)
⚡ KV Errors → KV Retry Duration ← 事务重试耗时
⚡ Transaction → Transaction OPS ← 事务吞吐
⚡ Transaction → Duration ← 事务延迟
常用 PromQL:
# 事务冲突率
sum(rate(tidb_tikvclient_txn_regions_num_sum{type="2pc_prewrite"}[1m]))
/
sum(rate(tidb_tikvclient_txn_regions_num_count{type="2pc_prewrite"}[1m]))
# Write Conflict 次数
sum(rate(tidb_session_retry_error_total[1m]))
10.3 常见问题 FAQ
Q:出现 Write Conflict 怎么办?
A:通常两种策略:
- 切换到悲观事务模式,让冲突提前暴露在 DML 阶段而非 commit 阶段
- 减少冲突概率:缩短事务执行时间、降低并发度、减小事务粒度
Q:transaction is too large 如何解决?
A:大事务拆分。参考上方"分批提交方案",将一次大批量写入拆分为多个小事务(推荐 3000 行/批)。大规模数据导入建议使用 TiDB Lightning 工具。
Q:TiDB 的 REPEATABLE READ 和 MySQL 有什么区别?
A:TiDB 基于 MVCC 快照实现,不会出现幻读;MySQL InnoDB 需要 Gap Locking 来防止幻读。TiDB 不支持 Gap Locking,但也不需要——因为快照隔离天然阻止幻读。
Q:TiDB 事务和 MySQL 一致吗?
A:95% 一致。主要差异:
- TiDB 不支持
READ UNCOMMITTED和SERIALIZABLE - TiDB 无 Gap Locking
- TiDB 的
SELECT LOCK IN SHARE MODE需要特殊配置 - TiDB 支持乐观模式的自动重试(MySQL 不支持)
十一、演进时间线
v1.0 ── 乐观事务模型(Percolator 2PC)
│
v3.0.8 ── 悲观事务支持 + 事务自动重试
│
v4.0 ── 1PC / Async Commit / Pipelined Pessimistic Lock
│
v5.0 ── Compaction Filter GC + Distributed GC
│
v6.0.0 ── In-Memory Pessimistic Lock
│ tidb_rc_read_check_ts(Read Committed 优化)
│
v6.3.0 ── tidb_rc_write_check_ts(RC 写入优化)
│
v7.x ── GC 持续优化(Compaction Filter 增强)
│
v8.x ── 事务性能持续优化,大对象存储支持(LOB)
参考文档
一句话总结:TiDB 基于 Google Percolator 模型,通过 TSO 全局时间戳 + MVCC + 两阶段提交(2PC)+ Primary Key 机制,在分布式 KV 存储 TiKV 之上实现了完全透明、强一致、水平扩展的 ACID 分布式事务。