按唯一键 update 一个不存在的值,语句没改到任何行,data_locks 里却出现 LOCK_GAP,紧接着的 insert 被挡住 3 秒。第一次遇到会往复杂处想,其实漏掉的多是个小前提。
SELECT * FROM performance_schema.data_locks WHERE OBJECT_NAME = 'users' AND LOCK_MODE LIKE '%GAP%'; SELECT @@transaction_isolation;
唯一索引的等值查询在记录不存在时,锁的是值应该落在的那个间隙,防止别人插进来。只有记录存在时才退化成单行记录锁。很多人以为唯一索引永远不会加间隙锁。这一点先确认清楚,后面就不用反复试。
「唯一索引间隙锁」相关的日志我一般连着看前后五分钟,单看一条报错经常判断不出是哪一步失败。把复现步骤压到三条以内,交给同事也能跑出来,才算真的定位清楚。
改动写完就同步给同事,下一个人踩同一个坑,成本还是你的。
具体做法是,确认查询值在表里确实不存在;业务允许就把隔离级别降到 RC 间隙锁不再加;插入路径上避免长事务占着这个间隙;用 data_locks 的 LOCK_DATA 看锁住的是哪段区间。
改完照着核一遍,不存在的唯一键查询不再阻塞后续 insert data_locks 里没有对应的间隙锁记录。
