rc级别下update只锁命中行且不锁间隙,但需索引有效;若未走索引则等同锁表;唯一约束等场景仍会隐式加间隙锁。

RC级别下UPDATE只锁命中行,不锁间隙
RC 默认不加间隙锁(Gap Lock),而 RR 会自动对索引范围加临键锁(Next-Key Lock)。比如表中有 id 值 1、5、10,执行 UPDATE t SET x=1 WHERE id BETWEEN 3 AND 8:
- 在 RR 下,InnoDB 会锁住 (1,5] 和 (5,10) 这两个间隙,导致插入
id=4或id=6被阻塞 - 在 RC 下,仅对实际命中的
id=5加记录锁(Record Lock),间隙完全开放,INSERT自由
间隙锁是 RR 防幻读的代价,RC 放弃它,换来了更窄的锁边界——但这只在索引有效时成立。
RC中非匹配行的锁执行完就释放
RC 的 UPDATE/DELETE 在语句执行结束后,立刻释放所有未满足 WHERE 条件的行锁;RR 则把扫描过的所有行(无论是否命中)都加上临键锁,并持有到事务结束。
- 例如:
UPDATE users SET status = 2 WHERE created_at > '2026-01-01',实际只改 3 行 - RC:只锁这 3 行,其余扫描行锁秒释
- RR:可能锁住几千行,其他事务更新任意一行都会等待
锁持有窗口越窄,事务间重叠冲突概率越低——这是 RC 提升并发最直接的机制。
没走索引时,RC 的“小锁”优势直接归零
RC 不加间隙锁的前提是“真正命中索引行”。一旦 WHERE 条件没走索引,InnoDB 仍要全表扫描,为每一行加记录锁,效果等同锁表。
-
EXPLAIN SELECT * FROM t WHERE user_id = '123' FOR UPDATE中key为NULL,说明没走索引 - 隐式类型转换:
user_id是INT,但传了字符串'123',索引失效 - 联合索引
INDEX(a, b, c)无法用于WHERE b = 10,哪怕b单独建过索引也无效
RC 的锁变小,不是靠隔离级别本身,而是靠执行计划精准——它只帮你省掉“不该锁的间隙”,但“不该锁的行”,还得你用索引和写法来兜底。
RC下仍可能意外加间隙锁的几个场景
RC 并非绝对无间隙锁。在唯一约束检查、外键校验等显式一致性保障环节,InnoDB 仍会加 gap lock,此时 INSERT 死锁仍可能发生。
- 向有唯一索引的字段插入重复值时,会加 gap lock 防止其他事务插入相同值
- 外键引用表执行 INSERT/UPDATE,需检查被引用值存在性,也可能触发间隙锁
- 使用
SELECT ... FOR UPDATE或LOCK IN SHARE MODE时,RC 仍只加 record lock,但若语句本身引发唯一冲突,则底层会补 gap lock
真正容易被忽略的是:你以为关了间隙锁就安全了,结果在唯一索引冲突路径上撞上隐式 gap lock——这个点不看执行计划、不查 SHOW ENGINE INNODB STATUS,几乎没法感知。











