在MySQL的InnoDB存储引擎中,REPEATABLE READ 隔离级别通过 MVCC(多版本并发控制) 和 Next-Key Lock(记录锁 + 间隙锁) 来实现事务的隔离性,尤其是避免 幻读(Phantom Read) 问题。下面详细解释这两种机制的作用及其如何避免幻读。
1. MVCC(多版本并发控制)
MVCC 是 InnoDB 实现高并发的重要机制,它通过保存数据的多个版本来实现非阻塞读操作。
MVCC 的核心思想
-
每个事务在开始时会被分配一个唯一的事务ID(
transaction_id
)。 -
每条记录会保存两个隐藏字段:
-
DB_TRX_ID
:记录最后一次修改它的事务ID。 -
DB_ROLL_PTR
:指向回滚段中的 undo log,用于重建旧版本数据。
-
-
事务在读取数据时,只能看到以下数据:
-
事务开始之前已经提交的数据。
-
当前事务自身修改的数据。
-
MVCC 如何避免幻读
-
在 REPEATABLE READ 隔离级别下,事务在第一次读取数据时会生成一个 一致性视图(Consistent View)。
-
后续的所有读操作都基于这个一致性视图,即使其他事务插入或删除了数据,当前事务也不会看到这些变化。
-
因此,MVCC 可以避免 不可重复读 和 幻读。
2. Next-Key Lock(记录锁 + 间隙锁)
Next-Key Lock 是 InnoDB 在 REPEATABLE READ 隔离级别下使用的一种锁机制,它结合了 记录锁(Record Lock) 和 间隙锁(Gap Lock)。
Next-Key Lock 的作用
-
记录锁(Record Lock):锁定索引记录。
-
间隙锁(Gap Lock):锁定索引记录之间的间隙,防止其他事务在间隙中插入新记录。
-
Next-Key Lock:锁定记录及其前后的间隙。
Next-Key Lock 如何避免幻读
-
当执行范围查询时,InnoDB 会对查询范围内的所有记录和间隙加锁。
-
例如,执行以下查询:
sql
复制
SELECT * FROM users WHERE age > 20 AND age < 30 FOR UPDATE;
InnoDB 会对
age > 20 AND age < 30
范围内的所有记录和间隙加锁,防止其他事务插入满足条件的新记录。 -
这样,即使其他事务尝试插入
age = 25
的新记录,也会被阻塞,从而避免幻读。
3. MVCC 和 Next-Key Lock 的协同作用
-
MVCC:
-
提供一致性视图,确保事务在读取数据时看到的是稳定的快照。
-
避免 不可重复读 和部分 幻读。
-
-
Next-Key Lock:
-
锁定记录和间隙,防止其他事务插入新记录。
-
完全避免 幻读。
-
示例
假设有一个表 users
,数据如下:
id | age |
---|---|
1 | 20 |
2 | 25 |
3 | 30 |
-
事务1:
sql
复制
START TRANSACTION; SELECT * FROM users WHERE age > 20 AND age < 30 FOR UPDATE;
- InnoDB 会对
age = 25
的记录加锁,并对(20, 25)
和(25, 30)
的间隙加锁。
- InnoDB 会对
-
事务2:
sql
复制
START TRANSACTION; INSERT INTO users (id, age) VALUES (4, 22); -- 阻塞,因为 age = 22 在 (20, 25) 的间隙中
- 由于间隙锁的存在,事务2的插入操作会被阻塞,从而避免幻读。
4. 为什么 REPEATABLE READ 能避免幻读?
-
MVCC 确保事务在读取数据时看到的是稳定的快照,避免了 不可重复读。
-
Next-Key Lock 锁定记录和间隙,防止其他事务插入新记录,避免了 幻读。
5. 总结
-
REPEATABLE READ 隔离级别通过 MVCC 和 Next-Key Lock 协同工作,实现了高并发下的数据一致性。
-
MVCC 提供一致性视图,避免不可重复读。
-
Next-Key Lock 锁定记录和间隙,完全避免幻读。
-
这种机制在保证数据一致性的同时,提供了较高的并发性能,是 MySQL 默认的隔离级别。