0
0
1
0
博客/.../

分布式数据库事务隔离级别详解?TiDB RC/RR/Snapshot 选择指南

 Billmay表妹  发表于  2026-06-02
原创

摘要

事务隔离级别是数据库并发控制的核心机制,直接影响数据一致性和系统性能。在分布式数据库场景下,隔离级别的实现更加复杂。本文从 ANSI SQL 标准出发,详解 TiDB 支持的隔离级别(RC、RR、Snapshot Isolation),对比乐观事务与悲观事务的适用场景,并提供不同业务场景下的隔离级别选择建议。本文适合正在设计分布式事务方案、需要理解 TiDB 事务行为的应用架构师和 DBA。


一、事务隔离级别基础概念

1.1 并发事务问题

当多个事务并发执行时,可能出现以下数据不一致问题:

问题类型 定义 严重程度
脏读(Dirty Read) 读取到其他事务未提交的数据
不可重复读(Non-Repeatable Read) 同一事务内两次读取同一行得到不同结果
幻读(Phantom Read) 同一事务内两次范围查询返回不同行数
写偏序(Write Skew) 两个事务各自读取对方写入的数据并更新,违反业务约束 中高

1.2 ANSI SQL 隔离级别

ANSI SQL-92 标准定义了四个隔离级别,从低到高依次为:

隔离级别 脏读 不可重复读 幻读 写偏序
Read Uncommitted 可能 可能 可能 可能
Read Committed (RC) 不可能 可能 可能 可能
Repeatable Read (RR) 不可能 不可能 可能* 不可能
Serializable 不可能 不可能 不可能 不可能

引用:\* 在 TiDB 的 RR 实现中,幻读也不会发生(详见下文)。

1.3 Snapshot Isolation(快照隔离)

Snapshot Isolation(SI)是一种在 RC 和 Serializable 之间的隔离级别,由 PORCELAIN 论文(2000 年)提出。SI 保证事务内的所有读取都基于事务开始时的一致性快照,不会出现脏读和不可重复读。但 SI 不防止写偏序(Write Skew)问题。

隔离级别 读一致性 写冲突检测 写偏序防护
Read Committed 语句级快照 行级
Snapshot Isolation 事务级快照 行级
Repeatable Read 事务级快照 行级+约束检测 有(TiDB 实现)
Serializable 事务级快照 完整依赖图

二、TiDB 支持的隔离级别

2.1 TiDB 隔离级别概览

TiDB 目前支持以下隔离级别:

隔离级别 实现方式 默认 说明
Read Committed (RC) Percolator + 语句级快照 每条语句读取最新已提交数据
Repeatable Read (RR) Percolator + 事务级快照 整个事务读取同一快照
Snapshot Isolation (SI) Percolator + 事务级快照 本质上等同 TiDB 的 RR

TiDB 不支持 Read Uncommitted 和 Serializable。 但 TiDB 的 RR 实现比标准定义更强——它不仅防止脏读和不可重复读,还通过悲观事务的锁机制防止幻读和写偏序。

-- 设置事务隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;

-- 查看当前隔离级别
SELECT @@transaction_isolation;
-- 或
SHOW VARIABLES LIKE 'transaction_isolation';

2.2 TiDB RC 隔离级别实现

TiDB 的 RC 使用语句级快照:每条 SQL 语句执行时获取最新的已提交数据版本。

事务 A:
  BEGIN;
  SELECT balance FROM accounts WHERE id = 1;  -- 读取快照 t1,balance = 1000
  
  -- 此时事务 B 提交了 UPDATE accounts SET balance = 900 WHERE id = 1
  
  SELECT balance FROM accounts WHERE id = 1;  -- RC 下:读取快照 t2,balance = 900
                                               -- RR 下:读取快照 t1,balance = 1000
  COMMIT;

RC 适用场景:

  • 对数据实时性要求高的业务(如库存扣减、余额查询)
  • 冲突较少的短事务
  • 需要"读已提交"语义的应用迁移

2.3 TiDB RR 隔离级别实现(默认)

TiDB 的 RR 使用事务级快照:整个事务从 `BEGIN` 开始获取一个一致性快照(通过 PD 获取当前 timestamp),事务内所有读操作都基于该快照。

RR 快照时间线:
  T0: 事务 A BEGIN(获取快照 timestamp = 100)
  T1: 事务 B UPDATE row1(commit at 105)
  T2: 事务 B UPDATE row2(commit at 110)
  T3: 事务 A SELECT row1 → 返回 timestamp ≤ 100 的版本
  T4: 事务 A SELECT row2 → 返回 timestamp ≤ 100 的版本
  T5: 事务 A COMMIT(commit at 120)

TiDB RR vs 标准 RR 的关键区别:

特性 ANSI SQL 标准 RR TiDB RR
不可重复读 防止 防止
幻读 可能发生 防止(通过悲观锁)
写偏序 可能发生 防止(通过悲观锁约束检测)
实现方式 锁定读取行 多版本快照 + 两阶段提交

三、TiDB 乐观事务与悲观事务

3.1 事务模型对比

TiDB 支持两种事务模型,通过 `tidb_txn_mode` 变量控制:

维度 乐观事务(Optimistic) 悲观事务(Pessimistic)
锁获取时机 COMMIT 阶段 DML 执行阶段
冲突处理 写冲突时重试 等待锁释放
默认模式 TiDB < 4.0 默认 TiDB ≥ 4.0 默认
适用场景 冲突少、写入分散 冲突多、热点更新
-- 设置事务模式
SET tidb_txn_mode = 'optimistic';
SET tidb_txn_mode = 'pessimistic';

-- 查看当前事务模式
SELECT @@tidb_txn_mode;

-- 乐观事务冲突重试示例
-- 出现 Write Conflict 错误时,应用需要自行重试
BEGIN OPTIMISTIC;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
-- 如果另一个事务同时修改了 id=1 的行,COMMIT 时会报错
COMMIT;
-- Error: [8025] Write conflict, retry later

3.2 悲观事务锁机制

悲观事务在执行 DML 时即获取锁:

操作 获取的锁类型 锁范围
SELECT ... FOR UPDATE 行级排他锁 选中的行
SELECT ... LOCK IN SHARE MODE 行级共享锁 选中的行
UPDATE/DELETE 行级排他锁 匹配的行
INSERT 行级排他锁 插入的行 + 唯一索引间隙
-- 悲观事务典型用法
BEGIN PESSIMISTIC;
SELECT balance FROM accounts WHERE id = 1 FOR UPDATE;  -- 立即加锁
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
COMMIT;  -- 释放锁

3.3 事务模式选择建议

业务特征 推荐事务模式 原因
金融账户转账(高冲突热点) 悲观事务 提前加锁避免写冲突重试
订单创建(冲突较少) 乐观事务 减少锁等待,提高吞吐
库存扣减(高并发同一 SKU) 悲观事务 热点行需要锁保护
日志写入(无冲突) 乐观事务 无需锁,最高性能
从 MySQL 迁移的业务 悲观事务 与 MySQL 行为一致,迁移风险低

四、不同隔离级别与性能关系

4.1 性能影响分析

隔离级别 读性能 写性能 内存开销 适用负载
RC 较高 较高 OLTP 高并发
RR(乐观) 高(冲突时下降) OLTP 低冲突
RR(悲观) 中(锁等待) 中(锁竞争) OLTP 高冲突

4.2 性能优化建议

-- 1. 避免大事务,减少锁持有时间和内存占用
-- 反模式:单事务更新大量数据
UPDATE orders SET status = 'done' WHERE create_time < '2024-01-01';  -- 影响百万行
-- 推荐:分批处理
SET tidb_batch_delete = ON;  -- 避免单事务过大
DELETE FROM orders WHERE create_time < '2024-01-01' LIMIT 1000;
-- 循环执行直到删除完毕

-- 2. 乐观事务设置重试次数
SET tidb_retry_limit = 10;  -- 默认 10 次
SET tidb_disable_txn_auto_retry = 0;  -- 开启自动重试

-- 3. 悲观事务设置锁等待超时
SET innodb_lock_wait_timeout = 50;  -- 默认 50 秒(秒为单位)

-- 4. 跳过锁检查(慎用,仅限特定场景)
SET tidb_skip_missing_lock_table = ON;

4.3 事务超时配置

参数 默认值 说明
`tidb_txn_commit_timeout` 50s 事务提交超时
`innodb_lock_wait_timeout` 50s 悲观锁等待超时
`tidb_retry_limit` 10 乐观事务重试上限
`max_execution_time` 0(无限制) 语句执行超时(ms)

五、FAQ

Q1:TiDB 的 RR 隔离级别和 MySQL 的 RR 有什么区别?

A:MySQL 的 InnoDB RR 基于间隙锁(Gap Lock)和 Next-Key Lock 实现,会锁定范围以防止幻读。TiDB 的 RR 基于多版本并发控制(MVCC),通过事务级快照保证一致性读取。在悲观事务模式下,TiDB 通过 `SELECT FOR UPDATE` 的行级锁也提供幻读防护。两者都能防止幻读,但实现机制不同。

Q2:为什么 TiDB 不支持 Serializable 隔离级别?

A:TiDB 的分布式事务基于 Percolator 协议,采用两阶段提交(2PC)和乐观并发控制。完整的 Serializable 实现需要全局依赖图检测,在分布式环境下性能开销极大。TiDB 的悲观事务 RR 在大多数实际场景中已经提供了足够强的一致性保证,包括防止幻读和写偏序。

Q3:从 MySQL 迁移到 TiDB,需要修改事务相关代码吗?

A:使用悲观事务模式(TiDB 4.0+ 默认)迁移时,绝大多数 MySQL 事务代码无需修改。需要注意的是:TiDB 不支持 `SAVEPOINT`、`XA 事务的 xa start/xa end` 等少数特性,以及大事务的性能特征与 MySQL 不同,建议控制单事务大小在合理范围内。

Q4:TiDB 事务的 MVCC 数据如何清理?

A:TiDB 的 GC(Garbage Collection)机制自动清理过期 MVCC 数据。默认 GC 生命周期为 `tidb_gc_life_time`(默认 10 分钟),意味着快照能保留最近 10 分钟的历史版本。如需更长保留时间,可调整该参数,但需注意存储空间增长。


六、总结

TiDB 提供了 RC 和 RR(含 Snapshot Isolation)两种隔离级别,通过乐观事务和悲观事务两种模型适配不同业务场景。默认的悲观事务 RR 模式提供了与 MySQL 高度兼容的事务行为,同时避免了传统分布式数据库中的写冲突问题。

在选择隔离级别时,建议:

  • 高并发 OLTP:优先使用默认的 RR + 悲观事务
  • 实时性要求高的查询:可考虑切换到 RC 隔离级别
  • 低冲突分析型写入:使用乐观事务获得更高吞吐
  • 金融级核心交易:使用悲观事务 + RR + 合理的超时和重试策略

七、下一步行动

行动 链接
下载 TiDB 分布式事务最佳实践指南(PDF) https://pingkai.cn/docs
30 分钟试用 TiDB 事务功能 https://tidb.com/try
获取 MySQL 到 TiDB 迁移事务兼容性检查清单 https://pingkai.cn/docs
申请 TiDB 金融行业事务方案咨询 https://pingkai.cn/contact
参与 TiDB 事务机制社区讨论 https://asktidb.com/

七、相关资源

0
0
1
0

版权声明:本文为 TiDB 社区用户原创文章,遵循 CC BY-NC-SA 4.0 版权协议,转载请附上原文出处链接和本声明。

评论
暂无评论