Skip to content

TiDB 分布式事务

分布式事务基本概念

事务的ACID特性

  • 原子性(Atomicity):事务作为一个整体被执行,要么全部成功,要么全部失败
  • 一致性(Consistency):事务执行前后,数据库从一个一致状态转换到另一个一致状态
  • 隔离性(Isolation):多个事务并发执行时,一个事务的执行不应影响其他事务
  • 持久性(Durability):事务一旦提交,其结果应该永久保存到数据库中

事务隔离级别

隔离级别脏读不可重复读幻读加锁读
读未提交(Read Uncommitted)✅ 可能✅ 可能✅ 可能❌ 否
读已提交(Read Committed)❌ 不可能✅ 可能✅ 可能❌ 否
可重复读(Repeatable Read)❌ 不可能❌ 不可能✅ 可能❌ 否
串行化(Serializable)❌ 不可能❌ 不可能❌ 不可能✅ 是

分布式事务的挑战

  1. 网络分区:分布式环境中网络可能出现分区,导致节点间通信失败
  2. 节点故障:分布式系统中节点可能发生故障
  3. 时钟同步:分布式系统中各节点的时钟可能不一致
  4. 数据一致性:保证分布式环境下的数据一致性是最大的挑战

TiDB 分布式事务实现机制

核心设计

TiDB 分布式事务采用 Percolator 模型,结合 两阶段提交(2PC)MVCC(多版本并发控制) 实现分布式事务。

关键组件

组件功能
TiDB Server接收客户端请求,生成执行计划,协调事务
PD管理全局事务 ID,维护集群元数据
TiKV存储数据,执行本地事务,参与两阶段提交
TSO(Timestamp Oracle)生成全局唯一的时间戳,由 PD 提供

两阶段提交流程

  1. 第一阶段:Prewrite(预写)

    • 选择一个 Primary Key 作为事务的主键
    • 向所有涉及的 TiKV 节点发送 Prewrite 请求
    • TiKV 节点检查数据冲突,锁定数据
    • TiKV 节点将数据写入预写日志
    • TiKV 节点返回 Prewrite 结果
  2. 第二阶段: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;

分布式事务性能优化

事务设计优化

  1. 缩小事务范围

    • 只包含必要的操作
    • 避免在事务中执行耗时操作(如网络请求、复杂计算)
    • 及时提交或回滚事务
  2. 减少事务冲突

    • 避免长时间持有锁
    • 优化 SQL 执行计划,减少锁竞争
    • 考虑使用乐观锁代替悲观锁
  3. 合理使用隔离级别

    • 根据业务需求选择合适的隔离级别
    • 优先使用较低的隔离级别(如 Read Committed)
    • 仅在必要时使用较高的隔离级别

SQL 优化

  1. 使用索引

    • 为查询条件和 JOIN 条件添加索引
    • 避免全表扫描
    • 定期优化索引
  2. 优化 UPDATE 和 DELETE 语句

    • 避免没有 WHERE 条件的 UPDATE 和 DELETE
    • 为 UPDATE 和 DELETE 语句的 WHERE 条件添加索引
    • 限制一次更新的数据量
  3. 避免大事务

    • 分割大事务为多个小事务
    • 使用批量操作代替单个操作
    • 考虑使用异步处理

配置优化

  1. 调整事务相关参数

    sql
    -- 调整事务超时时间
    SET GLOBAL tidb_txn_timeout = 300;
    
    -- 调整乐观锁重试次数
    SET GLOBAL tidb_retry_limit = 10;
    
    -- 调整预写线程数
    SET GLOBAL tikv_prewrite_threads = 8;
  2. 调整 TiKV 配置

    toml
    [storage]
    # 调整 RocksDB 写入缓冲大小
    write-buffer-size = "64MB"
    
    [raftstore]
    # 调整 Raft 并发数
    raft-max-inflight-msgs = 2048

分布式事务监控与诊断

事务监控指标

  1. TiDB 监控指标

    • tidb_transaction_duration:事务执行时间分布
    • tidb_transaction_commit_count:事务提交次数
    • tidb_transaction_rollback_count:事务回滚次数
    • tidb_transaction_lock_keys:事务锁定的键数量
  2. TiKV 监控指标

    • tikv_raft_propose:Raft 提案数
    • tikv_prewrite_total_duration:Prewrite 总耗时
    • tikv_commit_total_duration:Commit 总耗时
    • tikv_lock_resolve_total_duration:锁解析总耗时

事务诊断工具

  1. 使用 TiDB Dashboard

    • 事务页面:查看当前运行的事务
    • 锁页面:查看锁等待情况
    • 慢查询页面:查看慢事务
  2. 使用 SQL 诊断

    sql
    -- 查看当前运行的事务
    SELECT * FROM information_schema.tidb_running_transactions;
    
    -- 查看锁等待情况
    SELECT * FROM information_schema.tidb_trx_lock_waits;
    
    -- 查看事务历史
    SELECT * FROM information_schema.tidb_transaction_history;
  3. 使用 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 错误
  • 大量事务处于等待状态

处理方法

  1. 查看锁等待情况

    sql
    SELECT * FROM information_schema.tidb_trx_lock_waits;
  2. 分析锁持有事务

    sql
    SELECT * FROM information_schema.tidb_running_transactions WHERE trx_id = <blocking_trx_id>;
  3. 终止长时间运行的事务

    sql
    KILL TIDB <connection_id>;

事务超时问题

症状

  • 出现 Transaction timeout 错误
  • 事务自动回滚

处理方法

  1. 调整事务超时时间

    sql
    SET GLOBAL tidb_txn_timeout = 600;
  2. 优化事务逻辑,减少事务执行时间

  3. 分割大事务为多个小事务

乐观锁冲突

症状

  • 出现 Write conflict 错误
  • 事务自动重试

处理方法

  1. 调整重试次数

    sql
    SET GLOBAL tidb_retry_limit = 20;
  2. 减少事务冲突,优化并发访问

  3. 考虑使用悲观锁

    sql
    SELECT * FROM `table` WHERE `condition` FOR UPDATE;

大事务问题

症状

  • 事务执行时间过长
  • 占用大量系统资源
  • 可能导致集群不稳定

处理方法

  1. 分割大事务为多个小事务

  2. 使用批量操作

    sql
    -- 批量插入示例
    INSERT INTO `table` VALUES (1, 'a'), (2, 'b'), (3, 'c');
  3. 使用异步处理

  4. 考虑使用 TiDB Lightning 等工具处理大量数据导入

分布式事务最佳实践

设计最佳实践

  1. 避免长事务

    • 事务执行时间应控制在秒级
    • 避免在事务中执行外部调用
    • 及时提交或回滚事务
  2. 合理选择隔离级别

    • 大多数业务场景使用 Read Committed 即可
    • 金融等对一致性要求高的场景使用 Repeatable Read
    • 避免使用 Serializable,除非必要
  3. 使用乐观锁

    • TiDB 默认使用乐观锁
    • 乐观锁适合读多写少的场景
    • 悲观锁适合写多读少的场景

开发最佳实践

  1. 处理事务异常

    • 在应用程序中捕获事务异常
    • 实现重试机制
    • 记录详细的错误日志
  2. 使用连接池

    • 合理配置连接池大小
    • 及时关闭连接
    • 监控连接池使用情况
  3. 测试事务

    • 测试事务的正确性
    • 测试事务的性能
    • 测试事务的并发处理能力

运维最佳实践

  1. 监控事务指标

    • 设置事务相关告警
    • 定期分析事务执行情况
    • 及时处理异常事务
  2. 定期清理

    • 清理长时间未提交的事务
    • 清理过期的 MVCC 版本
    • 优化表结构和索引
  3. 容量规划

    • 根据事务量规划集群规模
    • 预留足够的存储空间
    • 考虑未来业务增长

分布式事务与其他数据库的差异

与 MySQL 的差异

特性TiDBMySQL
事务模型Percolator + 2PCMVCC + 2PC
默认隔离级别Repeatable ReadRepeatable Read
锁机制乐观锁为主,支持悲观锁悲观锁为主,支持乐观锁
分布式支持原生支持需通过 XA 或中间件
大事务支持有限制支持,但性能下降
事务超时可配置可配置

与 PostgreSQL 的差异

特性TiDBPostgreSQL
事务模型Percolator + 2PCMVCC + 2PC
隔离级别支持 4 种隔离级别支持 4 种隔离级别
锁机制乐观锁为主悲观锁为主
分布式支持原生支持需通过扩展或中间件
事务 ID全局唯一 TSO递增数字
并发控制MVCCMVCC

常见问题(FAQ)

Q1: TiDB 分布式事务的性能如何?

A1: TiDB 分布式事务的性能取决于多个因素:

  • 事务大小:小事务性能较好,大事务性能下降
  • 并发度:中等并发度(< 1000 TPS)性能较好
  • 网络延迟:网络延迟低时性能较好
  • 数据分布:数据分布均匀时性能较好

一般情况下,TiDB 分布式事务的性能可以满足大多数业务场景的需求。

Q2: 如何处理 TiDB 分布式事务的死锁?

A2: TiDB 会自动检测和解决死锁,处理方法包括:

  1. 等待超时:事务超过超时时间自动回滚
  2. 死锁检测:TiDB 会检测死锁并终止其中一个事务
  3. 乐观锁重试:自动重试乐观锁冲突的事务

Q3: TiDB 支持 XA 事务吗?

A3: TiDB 支持 XA 事务,但不建议在生产环境中使用,因为:

  • XA 事务性能较差
  • 增加系统复杂度
  • 可能导致资源泄漏

推荐使用 TiDB 原生的分布式事务。

Q4: 如何优化 TiDB 分布式事务的写入性能?

A4: 优化方法包括:

  1. 减少事务大小
  2. 增加并发度
  3. 优化 SQL 语句
  4. 调整配置参数
  5. 使用批量操作
  6. 考虑使用异步处理

Q5: TiDB 分布式事务的一致性如何保证?

A5: TiDB 通过以下机制保证分布式事务的一致性:

  1. 两阶段提交(2PC):确保所有节点要么都提交,要么都回滚
  2. MVCC:保证事务的隔离性
  3. TSO:提供全局唯一的时间戳,确保事务的顺序性
  4. Raft 协议:保证 TiKV 节点间的数据一致性
  5. 预写日志(WAL):保证数据的持久性

Q6: 如何监控 TiDB 分布式事务的执行情况?

A6: 可以通过以下方式监控:

  1. TiDB Dashboard:提供直观的事务监控界面
  2. Prometheus + Grafana:监控事务相关指标
  3. SQL 语句:查询系统表和视图
  4. 日志:查看 TiDB 和 TiKV 日志

Q7: TiDB 分布式事务支持哪些隔离级别?

A7: TiDB 支持四种隔离级别:

  • Read Uncommitted
  • Read Committed
  • Repeatable Read(默认)
  • Serializable

Q8: 如何处理 TiDB 分布式事务中的锁竞争问题?

A8: 处理方法包括:

  1. 优化事务逻辑,减少锁持有时间
  2. 调整事务隔离级别
  3. 使用乐观锁代替悲观锁
  4. 增加并发度,分散锁竞争
  5. 考虑使用分片键,避免热点数据