为什么 MySQL InnoDB 的 Repeatable Read 可以阻止幻读?
为什么 MySQL InnoDB 的 Repeatable Read 可以阻止幻读?
首先,幻读(Phantom Read) 指的是:在同一个事务内,前后两次执行相同的查询,第一次查询不到的行,第二次查询时却出现了。
例如:
- 小明查询
SELECT * FROM users WHERE age > 20
,返回 5 条数据。 - 小红在另一个事务里插入一条
INSERT INTO users VALUES (21岁用户)
并提交。 - 小明再查询,发现结果变成 6 条!这就是幻读。
在标准 SQL 定义下,可重复读(Repeatable Read) 只保证「同一事务内,对已存在的数据读到的内容不变」,但新插入的数据仍然可能影响查询结果,所以一般可重复读不能解决幻读。
然而,MySQL 的 InnoDB 通过「间隙锁(Gap Lock)」在可重复读级别下额外解决了幻读问题。
InnoDB 如何阻止幻读?—— 间隙锁(Gap Lock)
Gap Lock(间隙锁) 是 InnoDB 的Next-Key Lock 机制的一部分。它不仅锁住「已有的数据行」,还会锁住「数据之间的间隙」,从而防止新的插入。
示例:用间隙锁阻止幻读
- 小明启动事务
BEGIN;
- 小明执行
SELECT * FROM users WHERE age > 20 FOR UPDATE;
- InnoDB 发现这里是范围查询,于是加上间隙锁,不仅锁住已有的
age > 20
的用户,还会锁住这些用户之间的「空隙」。
- InnoDB 发现这里是范围查询,于是加上间隙锁,不仅锁住已有的
- 小红想插入
INSERT INTO users VALUES (21岁用户)
,但发现被锁住了,必须等小明的事务提交后才能继续。
💡 这样,MySQL InnoDB 通过锁住「数据之间的间隙」,让新的插入无法发生,从而避免了幻读!
总结
- 标准 SQL 的 Repeatable Read 不能阻止幻读,但 MySQL InnoDB 通过「间隙锁(Gap Lock)」实现了这个能力。
- 间隙锁会锁住「查询范围内的数据 + 数据之间的间隙」,防止新的数据插入,从而避免了「前后两次查询看到的结果不一致」。
- 但间隙锁可能影响并发性能,因为它会锁住较大的范围,使得插入操作被阻塞。
➡️ 这就是为什么 MySQL InnoDB 的 Repeatable Read 级别下「不会发生幻读」的原因!