MySQL 的事务隔离级别定义了多个并发事务在访问和修改相同数据时,彼此之间的可见性和影响程度。它解决了并发事务可能引发的三类核心问题:
- 脏读: 一个事务读取了另一个未提交事务修改的数据。
- 不可重复读: 一个事务内多次读取同一行数据,由于其他事务的修改并提交,导致前后读取的结果不一致。
- 幻读: 一个事务内多次执行相同的查询,由于其他事务的插入或删除并提交,导致前后查询结果集的行数不一致。
MySQL 遵循 SQL 标准,支持以下四种事务隔离级别,按隔离强度从低到高排序:
-
READ UNCOMMITTED (读未提交):
- 描述: 最低的隔离级别。
- 解决的问题: 无。
- 可能存在的问题: 脏读、不可重复读、幻读都可能发生。
- 原理: 一个事务可以看到其他事务尚未提交的修改。
- 使用场景: 非常少见,通常只在需要查看最新可能数据(即使未提交)且完全不在意数据一致性的极端场景使用。性能最高但风险最大。
-
READ COMMITTED (读已提交):
- 描述: 大多数数据库系统(如 Oracle, SQL Server, PostgreSQL)的默认隔离级别(但 MySQL InnoDB 默认不是它)。
- 解决的问题: 脏读。
- 可能存在的问题: 不可重复读、幻读。
- 原理: 一个事务只能看到其他事务已经提交的修改。它保证读取到的任何数据都是已提交的数据。
- 实现 (InnoDB): 通常使用一致性非锁定读(快照读)。在
SELECT
语句执行时(或事务内第一次读时)创建该语句的快照(Read View),基于这个快照读取数据。其他事务的提交在本次查询执行后对本事务的后续查询才可见(除非使用FOR UPDATE
/LOCK IN SHARE MODE
加锁)。 - 使用场景: 比
READ UNCOMMITTED
安全很多,是很多应用的合理选择,特别是当应用逻辑能够容忍不可重复读和幻读时。性能较好。
-
REPEATABLE READ (可重复读):
- 描述: MySQL InnoDB 存储引擎的默认隔离级别。
- 解决的问题: 脏读、不可重复读。
- 可能存在的问题: 幻读(但在 InnoDB 中,通过
Next-Key Locks
机制在大多数情况下避免了幻读)。 - 原理: 保证在同一个事务中多次读取同一行数据的结果是一致的。
- 实现 (InnoDB):
- 一致性非锁定读 (快照读): 在事务中第一次执行
SELECT
语句时创建一个整个事务的一致性快照(Read View)。在该事务后续的所有普通SELECT
操作都会基于这个同一个快照来读取数据。因此,即使其他事务修改并提交了数据,本事务内看到的仍然是它开始时那个“快照”版本的数据,从而避免了不可重复读。 - 锁定读 (当前读): 当执行
SELECT ... FOR UPDATE
、SELECT ... LOCK IN SHARE MODE
、UPDATE
、DELETE
语句时,InnoDB 会使用Next-Key Locks。这种锁不仅锁住扫描到的索引记录,还会锁住这些记录之前的“间隙”,防止其他事务在锁定的范围内插入新行。正是这种机制在很大程度上防止了幻读的发生。
- 一致性非锁定读 (快照读): 在事务中第一次执行
- 使用场景: 需要保证事务内多次读取相同数据结果一致的场景(如对账、报表生成)。是 MySQL InnoDB 的默认且推荐级别,在保证较高一致性同时提供较好性能。
-
SERIALIZABLE (可串行化):
- 描述: 最高的隔离级别。
- 解决的问题: 脏读、不可重复读、幻读。
- 可能存在的问题: 性能最低(并发度最低),可能导致大量的锁等待甚至死锁。
- 原理: 通过强制事务串行执行(而非并发执行)来避免所有并发问题。它会在读取的每一行数据上都加锁(通常是共享锁)。
- 实现 (InnoDB): 将所有的普通
SELECT
语句隐式转换为SELECT ... LOCK IN SHARE MODE
,即对读取的数据加共享锁。这会导致其他事务无法修改这些数据,写操作会被阻塞。写操作(UPDATE
,DELETE
,INSERT
)仍然会对涉及的行加排他锁。 - 使用场景: 要求最高级别的数据一致性,且可以接受显著性能下降的场景。如金融核心交易等。
总结对比表:
隔离级别 | 脏读 | 不可重复读 | 幻读 (InnoDB) | 性能 | 并发度 |
---|---|---|---|---|---|
READ UNCOMMITTED | ✅ | ✅ | ✅ | 最高 | 最高 |
READ COMMITTED | ❌ | ✅ | ✅ | 较高 | 较高 |
REPEATABLE READ | ❌ | ❌ | 大部分避免 (Next-Key Lock) | 中等 | 中等 |
SERIALIZABLE | ❌ | ❌ | ❌ | 最低 | 最低 |
查看和设置隔离级别:
-
查看当前会话隔离级别:
SELECT @@transaction_isolation; -- MySQL 8.0+ -- 或 SELECT @@tx_isolation; -- MySQL 5.x
-
查看全局隔离级别:
SELECT @@global.transaction_isolation; -- MySQL 8.0+ -- 或 SELECT @@global.tx_isolation; -- MySQL 5.x
-
设置当前会话隔离级别 (仅影响当前连接):
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; -- 替换为需要的级别
-
设置全局隔离级别 (影响之后的所有新连接,需要相应权限):
SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED; -- 替换为需要的级别
-
在启动时设置 (修改配置文件
my.cnf
/my.ini
):[mysqld] transaction-isolation = READ-COMMITTED # 替换为需要的级别
选择建议:
- 优先使用默认 (
REPEATABLE READ
): MySQL InnoDB 的默认REPEATABLE READ
在性能和一致性之间取得了很好的平衡,并通过Next-Key Locks
有效避免了幻读,适合绝大多数应用场景。 - 需要更强实时性且容忍不可重复读/幻读: 考虑
READ COMMITTED
。这在某些需要看到其他事务最新提交结果的场景(如消息通知)可能更合适。 - 最高一致性要求: 仅在绝对必要且完全理解性能代价时使用
SERIALIZABLE
。 - 避免使用
READ UNCOMMITTED
: 除非有非常特殊且可控的场景需求。
理解事务隔离级别对于设计高性能、高一致性的数据库应用至关重要。务必根据你的具体应用需求来选择最合适的隔离级别。