外观
TiDB 分布式事务
分布式事务基本概念
事务的ACID特性
- 原子性(Atomicity):事务作为一个整体被执行,要么全部成功,要么全部失败
- 一致性(Consistency):事务执行前后,数据库从一个一致状态转换到另一个一致状态
- 隔离性(Isolation):多个事务并发执行时,一个事务的执行不应影响其他事务
- 持久性(Durability):事务一旦提交,其结果应该永久保存到数据库中
事务隔离级别
| 隔离级别 | 脏读 | 不可重复读 | 幻读 | 加锁读 |
|---|---|---|---|---|
| 读未提交(Read Uncommitted) | ✅ 可能 | ✅ 可能 | ✅ 可能 | ❌ 否 |
| 读已提交(Read Committed) | ❌ 不可能 | ✅ 可能 | ✅ 可能 | ❌ 否 |
| 可重复读(Repeatable Read) | ❌ 不可能 | ❌ 不可能 | ✅ 可能 | ❌ 否 |
| 串行化(Serializable) | ❌ 不可能 | ❌ 不可能 | ❌ 不可能 | ✅ 是 |
分布式事务的挑战
- 网络分区:分布式环境中网络可能出现分区,导致节点间通信失败
- 节点故障:分布式系统中节点可能发生故障
- 时钟同步:分布式系统中各节点的时钟可能不一致
- 数据一致性:保证分布式环境下的数据一致性是最大的挑战
TiDB 分布式事务实现机制
核心设计
TiDB 分布式事务采用 Percolator 模型,结合 两阶段提交(2PC) 和 MVCC(多版本并发控制) 实现分布式事务。
关键组件
| 组件 | 功能 |
|---|---|
| TiDB Server | 接收客户端请求,生成执行计划,协调事务 |
| PD | 管理全局事务 ID,维护集群元数据 |
| TiKV | 存储数据,执行本地事务,参与两阶段提交 |
| TSO(Timestamp Oracle) | 生成全局唯一的时间戳,由 PD 提供 |
两阶段提交流程
第一阶段:Prewrite(预写)
- 选择一个 Primary Key 作为事务的主键
- 向所有涉及的 TiKV 节点发送 Prewrite 请求
- TiKV 节点检查数据冲突,锁定数据
- TiKV 节点将数据写入预写日志
- TiKV 节点返回 Prewrite 结果
第二阶段:Commit(提交)
- 向 PD 请求全局提交时间戳
- 向 Primary Key 所在的 TiKV 节点发送 Commit 请求
- Primary Key 所在节点提交事务,释放锁
- 异步清理其他节点的锁和预写日志
MVCC 实现
- TiDB 使用 版本号 标识数据的不同版本
- 每个事务开始时获取一个 start_ts 作为事务的读取快照
- 事务提交时获取一个 commit_ts 作为事务的提交版本
- 读取操作通过比较版本号,选择可见的数据版本
分布式事务使用方法
基本使用
sql
-- 显式事务
BEGIN;
INSERT INTO users (id, name) VALUES (1, 'user1');
UPDATE accounts SET balance = balance - 100 WHERE user_id = 1;
COMMIT;
-- 或使用 ROLLBACK 回滚事务
BEGIN;
INSERT INTO users (id, name) VALUES (2, 'user2');
ROLLBACK;
-- 隐式事务(默认自动提交)
INSERT INTO users (id, name) VALUES (3, 'user3');设置事务隔离级别
sql
-- 设置会话级别的隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
-- 设置全局级别的隔离级别
SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;
-- 查看当前隔离级别
SELECT @@tx_isolation;事务控制语句
| 语句 | 功能 |
|---|---|
BEGIN / START TRANSACTION | 开始事务 |
COMMIT | 提交事务 |
ROLLBACK | 回滚事务 |
SAVEPOINT | 创建保存点 |
ROLLBACK TO SAVEPOINT | 回滚到保存点 |
RELEASE SAVEPOINT | 释放保存点 |
事务示例
sql
-- 银行转账示例
BEGIN;
-- 1. 检查余额是否充足
SELECT balance FROM accounts WHERE user_id = 1 FOR UPDATE;
-- 2. 扣除转出账户余额
UPDATE accounts SET balance = balance - 1000 WHERE user_id = 1;
-- 3. 增加转入账户余额
UPDATE accounts SET balance = balance + 1000 WHERE user_id = 2;
-- 4. 记录转账日志
INSERT INTO transfer_logs (from_user, to_user, amount) VALUES (1, 2, 1000);
-- 5. 提交事务
COMMIT;分布式事务性能优化
事务设计优化
缩小事务范围
- 只包含必要的操作
- 避免在事务中执行耗时操作(如网络请求、复杂计算)
- 及时提交或回滚事务
减少事务冲突
- 避免长时间持有锁
- 优化 SQL 执行计划,减少锁竞争
- 考虑使用乐观锁代替悲观锁
合理使用隔离级别
- 根据业务需求选择合适的隔离级别
- 优先使用较低的隔离级别(如 Read Committed)
- 仅在必要时使用较高的隔离级别
SQL 优化
使用索引
- 为查询条件和 JOIN 条件添加索引
- 避免全表扫描
- 定期优化索引
优化 UPDATE 和 DELETE 语句
- 避免没有 WHERE 条件的 UPDATE 和 DELETE
- 为 UPDATE 和 DELETE 语句的 WHERE 条件添加索引
- 限制一次更新的数据量
避免大事务
- 分割大事务为多个小事务
- 使用批量操作代替单个操作
- 考虑使用异步处理
配置优化
调整事务相关参数
sql-- 调整事务超时时间 SET GLOBAL tidb_txn_timeout = 300; -- 调整乐观锁重试次数 SET GLOBAL tidb_retry_limit = 10; -- 调整预写线程数 SET GLOBAL tikv_prewrite_threads = 8;调整 TiKV 配置
toml[storage] # 调整 RocksDB 写入缓冲大小 write-buffer-size = "64MB" [raftstore] # 调整 Raft 并发数 raft-max-inflight-msgs = 2048
分布式事务监控与诊断
事务监控指标
TiDB 监控指标
tidb_transaction_duration:事务执行时间分布tidb_transaction_commit_count:事务提交次数tidb_transaction_rollback_count:事务回滚次数tidb_transaction_lock_keys:事务锁定的键数量
TiKV 监控指标
tikv_raft_propose:Raft 提案数tikv_prewrite_total_duration:Prewrite 总耗时tikv_commit_total_duration:Commit 总耗时tikv_lock_resolve_total_duration:锁解析总耗时
事务诊断工具
使用 TiDB Dashboard
- 事务页面:查看当前运行的事务
- 锁页面:查看锁等待情况
- 慢查询页面:查看慢事务
使用 SQL 诊断
sql-- 查看当前运行的事务 SELECT * FROM information_schema.tidb_running_transactions; -- 查看锁等待情况 SELECT * FROM information_schema.tidb_trx_lock_waits; -- 查看事务历史 SELECT * FROM information_schema.tidb_transaction_history;使用 TiDB 系统表
sql-- 查看事务统计信息 SELECT * FROM performance_schema.events_statements_summary_by_digest WHERE digest_text LIKE '%BEGIN%'; -- 查看事务锁统计 SELECT * FROM performance_schema.data_locks;
分布式事务常见问题处理
锁等待问题
症状:
- 事务执行缓慢
- 出现
Lock wait timeout错误 - 大量事务处于等待状态
处理方法:
查看锁等待情况
sqlSELECT * FROM information_schema.tidb_trx_lock_waits;分析锁持有事务
sqlSELECT * FROM information_schema.tidb_running_transactions WHERE trx_id = <blocking_trx_id>;终止长时间运行的事务
sqlKILL TIDB <connection_id>;
事务超时问题
症状:
- 出现
Transaction timeout错误 - 事务自动回滚
处理方法:
调整事务超时时间
sqlSET GLOBAL tidb_txn_timeout = 600;优化事务逻辑,减少事务执行时间
分割大事务为多个小事务
乐观锁冲突
症状:
- 出现
Write conflict错误 - 事务自动重试
处理方法:
调整重试次数
sqlSET GLOBAL tidb_retry_limit = 20;减少事务冲突,优化并发访问
考虑使用悲观锁
sqlSELECT * FROM `table` WHERE `condition` FOR UPDATE;
大事务问题
症状:
- 事务执行时间过长
- 占用大量系统资源
- 可能导致集群不稳定
处理方法:
分割大事务为多个小事务
使用批量操作
sql-- 批量插入示例 INSERT INTO `table` VALUES (1, 'a'), (2, 'b'), (3, 'c');使用异步处理
考虑使用 TiDB Lightning 等工具处理大量数据导入
分布式事务最佳实践
设计最佳实践
避免长事务
- 事务执行时间应控制在秒级
- 避免在事务中执行外部调用
- 及时提交或回滚事务
合理选择隔离级别
- 大多数业务场景使用
Read Committed即可 - 金融等对一致性要求高的场景使用
Repeatable Read - 避免使用
Serializable,除非必要
- 大多数业务场景使用
使用乐观锁
- TiDB 默认使用乐观锁
- 乐观锁适合读多写少的场景
- 悲观锁适合写多读少的场景
开发最佳实践
处理事务异常
- 在应用程序中捕获事务异常
- 实现重试机制
- 记录详细的错误日志
使用连接池
- 合理配置连接池大小
- 及时关闭连接
- 监控连接池使用情况
测试事务
- 测试事务的正确性
- 测试事务的性能
- 测试事务的并发处理能力
运维最佳实践
监控事务指标
- 设置事务相关告警
- 定期分析事务执行情况
- 及时处理异常事务
定期清理
- 清理长时间未提交的事务
- 清理过期的 MVCC 版本
- 优化表结构和索引
容量规划
- 根据事务量规划集群规模
- 预留足够的存储空间
- 考虑未来业务增长
分布式事务与其他数据库的差异
与 MySQL 的差异
| 特性 | TiDB | MySQL |
|---|---|---|
| 事务模型 | Percolator + 2PC | MVCC + 2PC |
| 默认隔离级别 | Repeatable Read | Repeatable Read |
| 锁机制 | 乐观锁为主,支持悲观锁 | 悲观锁为主,支持乐观锁 |
| 分布式支持 | 原生支持 | 需通过 XA 或中间件 |
| 大事务支持 | 有限制 | 支持,但性能下降 |
| 事务超时 | 可配置 | 可配置 |
与 PostgreSQL 的差异
| 特性 | TiDB | PostgreSQL |
|---|---|---|
| 事务模型 | Percolator + 2PC | MVCC + 2PC |
| 隔离级别 | 支持 4 种隔离级别 | 支持 4 种隔离级别 |
| 锁机制 | 乐观锁为主 | 悲观锁为主 |
| 分布式支持 | 原生支持 | 需通过扩展或中间件 |
| 事务 ID | 全局唯一 TSO | 递增数字 |
| 并发控制 | MVCC | MVCC |
常见问题(FAQ)
Q1: TiDB 分布式事务的性能如何?
A1: TiDB 分布式事务的性能取决于多个因素:
- 事务大小:小事务性能较好,大事务性能下降
- 并发度:中等并发度(< 1000 TPS)性能较好
- 网络延迟:网络延迟低时性能较好
- 数据分布:数据分布均匀时性能较好
一般情况下,TiDB 分布式事务的性能可以满足大多数业务场景的需求。
Q2: 如何处理 TiDB 分布式事务的死锁?
A2: TiDB 会自动检测和解决死锁,处理方法包括:
- 等待超时:事务超过超时时间自动回滚
- 死锁检测:TiDB 会检测死锁并终止其中一个事务
- 乐观锁重试:自动重试乐观锁冲突的事务
Q3: TiDB 支持 XA 事务吗?
A3: TiDB 支持 XA 事务,但不建议在生产环境中使用,因为:
- XA 事务性能较差
- 增加系统复杂度
- 可能导致资源泄漏
推荐使用 TiDB 原生的分布式事务。
Q4: 如何优化 TiDB 分布式事务的写入性能?
A4: 优化方法包括:
- 减少事务大小
- 增加并发度
- 优化 SQL 语句
- 调整配置参数
- 使用批量操作
- 考虑使用异步处理
Q5: TiDB 分布式事务的一致性如何保证?
A5: TiDB 通过以下机制保证分布式事务的一致性:
- 两阶段提交(2PC):确保所有节点要么都提交,要么都回滚
- MVCC:保证事务的隔离性
- TSO:提供全局唯一的时间戳,确保事务的顺序性
- Raft 协议:保证 TiKV 节点间的数据一致性
- 预写日志(WAL):保证数据的持久性
Q6: 如何监控 TiDB 分布式事务的执行情况?
A6: 可以通过以下方式监控:
- TiDB Dashboard:提供直观的事务监控界面
- Prometheus + Grafana:监控事务相关指标
- SQL 语句:查询系统表和视图
- 日志:查看 TiDB 和 TiKV 日志
Q7: TiDB 分布式事务支持哪些隔离级别?
A7: TiDB 支持四种隔离级别:
- Read Uncommitted
- Read Committed
- Repeatable Read(默认)
- Serializable
Q8: 如何处理 TiDB 分布式事务中的锁竞争问题?
A8: 处理方法包括:
- 优化事务逻辑,减少锁持有时间
- 调整事务隔离级别
- 使用乐观锁代替悲观锁
- 增加并发度,分散锁竞争
- 考虑使用分片键,避免热点数据
