分布式事务 #
一、事务基础 #
1.1 ACID特性 #
text
事务 ACID 特性
┌─────────────────────────────────────────────────────────────┐
│ │
│ Atomicity (原子性) │
│ ├── 事务是不可分割的工作单位 │
│ ├── 要么全部成功,要么全部失败 │
│ └── TiDB: 通过两阶段提交保证 │
│ │
│ Consistency (一致性) │
│ ├── 事务使数据库从一个一致状态变到另一个一致状态 │
│ └── TiDB: 通过约束检查保证 │
│ │
│ Isolation (隔离性) │
│ ├── 多个事务并发执行时互不干扰 │
│ └── TiDB: 支持多种隔离级别 │
│ │
│ Durability (持久性) │
│ ├── 事务提交后永久保存 │
│ └── TiDB: 通过 Raft 日志持久化保证 │
│ │
└─────────────────────────────────────────────────────────────┘
1.2 基本事务操作 #
sql
-- 开始事务
BEGIN;
-- 或
START TRANSACTION;
-- 提交事务
COMMIT;
-- 回滚事务
ROLLBACK;
-- 事务示例
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT;
二、隔离级别 #
2.1 支持的隔离级别 #
text
TiDB 隔离级别
┌─────────────────────────────────────────────────────────────┐
│ │
│ REPEATABLE READ (默认) │
│ ├── 可重复读 │
│ ├── 同一事务内多次读取结果相同 │
│ ├── 使用 MVCC 实现 │
│ └── 无幻读问题 │
│ │
│ READ COMMITTED │
│ ├── 读已提交 │
│ ├── 只能读取已提交的数据 │
│ └── 可能出现不可重复读 │
│ │
└─────────────────────────────────────────────────────────────┘
2.2 设置隔离级别 #
sql
-- 查看当前隔离级别
SELECT @@transaction_isolation;
-- 设置全局隔离级别
SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ;
SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;
-- 设置会话隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
-- 设置下个事务隔离级别
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
-- 在事务开始时设置
START TRANSACTION WITH CONSISTENT SNAPSHOT;
2.3 隔离级别示例 #
sql
-- 可重复读示例
-- 会话1
BEGIN;
SELECT balance FROM accounts WHERE id = 1; -- 结果: 1000
-- 会话2
UPDATE accounts SET balance = 2000 WHERE id = 1;
COMMIT;
-- 会话1 (再次查询)
SELECT balance FROM accounts WHERE id = 1; -- 结果: 1000 (不变)
COMMIT;
-- 会话1 (新事务)
BEGIN;
SELECT balance FROM accounts WHERE id = 1; -- 结果: 2000 (新值)
COMMIT;
三、事务模型 #
3.1 乐观事务 #
text
乐观事务流程
┌─────────────────────────────────────────────────────────────┐
│ │
│ 1. 获取 start_ts │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ BEGIN → 从 PD 获取 start_ts │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 2. 执行 SQL (本地缓存) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ INSERT/UPDATE/DELETE │ │
│ │ - 只在 TiDB Server 本地缓存 │ │
│ │ - 不写入 TiKV │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 3. COMMIT (两阶段提交) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ Prewrite: │ │
│ │ - 检查冲突 │ │
│ │ - 写入锁和数据 │ │
│ │ │ │
│ │ Commit: │ │
│ │ - 获取 commit_ts │ │
│ │ - 提交 Primary Key │ │
│ │ - 异步提交 Secondary Keys │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 特点: │
│ - 提交时检测冲突 │
│ - 适合冲突少的场景 │
│ - 冲突时需要重试 │
│ │
└─────────────────────────────────────────────────────────────┘
3.2 悲观事务 #
text
悲观事务流程
┌─────────────────────────────────────────────────────────────┐
│ │
│ 1. 获取 start_ts │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ BEGIN → 从 PD 获取 start_ts │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 2. 执行 SQL (立即加锁) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ SELECT ... FOR UPDATE │ │
│ │ - 立即获取行锁 │ │
│ │ - 阻止其他事务修改 │ │
│ │ │ │
│ │ UPDATE/DELETE │ │
│ │ - 执行时加锁 │ │
│ │ - 直接写入 TiKV │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 3. COMMIT │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ - 释放锁 │ │
│ │ - 提交数据 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 特点: │
│ - 执行时加锁 │
│ - 适合冲突多的场景 │
│ - 避免提交失败 │
│ │
└─────────────────────────────────────────────────────────────┘
3.3 选择事务模式 #
sql
-- 启用悲观事务
SET SESSION tidb_txn_mode = 'pessimistic';
-- 启用乐观事务
SET SESSION tidb_txn_mode = 'optimistic';
-- 全局设置
SET GLOBAL tidb_txn_mode = 'pessimistic';
text
事务模式选择建议
┌─────────────────────────────────────────────────────────────┐
│ │
│ 乐观事务适合: │
│ ├── 读多写少 │
│ ├── 冲突较少 │
│ ├── 批量操作 │
│ └── 数据导入 │
│ │
│ 悲观事务适合: │
│ ├── 写多冲突多 │
│ ├── 需要严格串行化 │
│ ├── 金融交易 │
│ └── 库存扣减 │
│ │
└─────────────────────────────────────────────────────────────┘
四、两阶段提交 #
4.1 提交流程 #
text
两阶段提交流程
┌─────────────────────────────────────────────────────────────┐
│ │
│ Phase 1: Prewrite │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 对每个修改的 Key: │ │
│ │ │ │
│ │ 1. 检查冲突 │ │
│ │ - 检查是否有其他事务已提交 │ │
│ │ - 检查是否有锁 │ │
│ │ │ │
│ │ 2. 写入数据 │ │
│ │ - 写入 start_ts 版本的数据 │ │
│ │ │ │
│ │ 3. 写入锁 │ │
│ │ - Primary Lock: 主锁 │ │
│ │ - Secondary Lock: 从锁 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ Phase 2: Commit │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 1. 获取 commit_ts │ │
│ │ - 从 PD 获取全局时间戳 │ │
│ │ │ │
│ │ 2. 提交 Primary Key │ │
│ │ - 写入 commit_ts │ │
│ │ - 清除 Primary Lock │ │
│ │ - 此时事务提交成功 │ │
│ │ │ │
│ │ 3. 异步提交 Secondary Keys │ │
│ │ - 后台异步清除 Secondary Lock │ │
│ │ - 不影响事务成功 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
4.2 异步提交 #
sql
-- 启用异步提交 (默认开启)
SET SESSION tidb_enable_async_commit = ON;
-- 异步提交优化
-- Primary Key 提交成功即返回
-- Secondary Keys 异步提交
-- 减少提交延迟
4.3 1PC优化 #
sql
-- 单阶段提交优化
-- 适用于只涉及一个 Region 的事务
SET SESSION tidb_enable_1pc = ON;
-- 1PC 跳过 Prewrite 阶段
-- 直接提交,减少延迟
五、事务最佳实践 #
5.1 事务大小控制 #
sql
-- 问题: 大事务
-- - 占用大量内存
-- - 长时间持有锁
-- - 可能超时
-- 解决: 分批提交
-- 批次1
BEGIN;
UPDATE orders SET status = 'processed'
WHERE status = 'pending' LIMIT 1000;
COMMIT;
-- 批次2
BEGIN;
UPDATE orders SET status = 'processed'
WHERE status = 'pending' LIMIT 1000;
COMMIT;
5.2 避免长事务 #
sql
-- 问题: 长事务
-- - 阻塞其他事务
-- - 占用资源
-- - 可能失败
-- 解决: 及时提交
BEGIN;
-- 只做必要的操作
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT;
-- 不要在事务中做耗时操作
5.3 死锁处理 #
sql
-- 死锁示例
-- 会话1
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
-- 会话2
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 2;
-- 会话1 (等待)
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
-- 会话2 (死锁)
UPDATE accounts SET balance = balance + 100 WHERE id = 1;
-- Error: Deadlock found
-- 解决: 按相同顺序访问资源
-- 始终按 id 顺序更新
六、事务监控 #
6.1 查看运行中的事务 #
sql
-- 查看当前运行的事务
SELECT * FROM INFORMATION_SCHEMA.TIDB_TRX;
-- 查看锁等待
SELECT * FROM INFORMATION_SCHEMA.DATA_LOCK_WAITS;
-- 查看死锁信息
SELECT * FROM INFORMATION_SCHEMA.DEADLOCKS;
6.2 事务相关配置 #
sql
-- 事务超时时间
SET SESSION tidb_txn_wait_timeout = 60000; -- 60秒
-- 语句超时时间
SET SESSION max_execution_time = 30000; -- 30秒
-- GC Life Time (影响历史数据可见性)
SET GLOBAL tidb_gc_life_time = '10m';
七、总结 #
分布式事务要点:
| 模块 | 要点 |
|---|---|
| ACID | 原子性、一致性、隔离性、持久性 |
| 隔离级别 | REPEATABLE READ、READ COMMITTED |
| 事务模型 | 乐观事务、悲观事务 |
| 两阶段提交 | Prewrite + Commit |
| 最佳实践 | 控制大小、避免长事务、防止死锁 |
下一步,让我们学习数据复制和高可用!
最后更新:2026-03-27