MySQL RR级别不彻底解决幻读,仅当当前读+索引列+范围条件三者齐备时,InnoDB才启用Next-Key Lock锁住间隙;缺一即失效,导致幻读发生。

MySQL 的 REPEATABLE READ 隔离级别本身不自动防幻读,必须满足「当前读 + 索引列 + 范围条件」三者齐备,InnoDB 才会启用 Next-Key Lock(即记录锁 + 间隙锁),真正锁住查询范围内的间隙。缺一不可,否则照样发生幻读。
为什么 status='pending' 查不到幻读,而 id > 10 可以?
核心在索引与查询类型是否触发间隙锁:
-
id > 10:若id是主键或有索引,且是范围条件,InnoDB 会锁住 (10, +∞) 这个间隙,后续INSERT INTO t VALUES (15, ...)会被阻塞 -
status = 'pending':若status字段无索引,InnoDB 全表扫描,无法界定“pending”落在哪段间隙,间隙锁压根不启动;即使有索引,等值查询 +LOCK IN SHARE MODE默认只加 S 类记录锁,不锁间隙 - 隐式转换也会失效:比如
WHERE DATE(create_time) = '2026-04-17'导致索引失效,间隙锁退化为无
怎么确认 Next-Key Lock 真的生效了?
不能只看隔离级别设成 RR,得查锁的实际行为:
- 查
performance_schema.data_locks表,过滤当前事务 ID:SELECT LOCK_TRX_ID, LOCK_MODE, LOCK_DATA FROM performance_schema.data_locks WHERE LOCK_TRX_ID = '你的事务ID'; - 关键看
LOCK_MODE:出现RECORD & GAP或RECORD & NEXT-KEY才算成功;如果只有RECORD,说明只是行锁;如果没记录,可能连锁都没加 - 实操验证:事务 A 执行
SELECT * FROM t WHERE id > 5 FOR UPDATE,事务 B 尝试INSERT INTO t VALUES (6, ...)—— 若被阻塞,说明间隙已锁住
哪些写法会让间隙锁“静默失效”?
Next-Key Lock 极其依赖运行时上下文,稍有偏差就降级:
- MySQL 8.0+ 中残留配置
innodb_locks_unsafe_for_binlog = ON(已废弃但旧实例可能仍开着),会强制禁用 Gap Lock -
SELECT ... LOCK IN SHARE MODE+ 唯一索引等值查询 → 只加 S 类 Record Lock,不锁间隙 - 查询条件含函数、类型隐式转换(如
WHERE phone + '' = '138...')、OR 条件导致索引失效,间隙锁直接不触发 - 使用
READ COMMITTED隔离级别:该级别下 InnoDB 不启用间隙锁,即使加FOR UPDATE也只锁行,幻读必现
真正防住幻读的关键不在“设了 RR”,而在“让每一条当前读语句都实际锁住间隙”——这意味着你得检查执行计划是否走索引、确认条件是否为范围、验证锁模式是否含 GAP,漏掉任意一环,幻读就藏在下次查询里等着你。


















