mysql间隙锁是什么_mysql锁原理解析

来源:这里教程网 时间:2026-02-28 20:38:44 作者:

间隙锁是啥?一句话说清

间隙锁(

Gap Lock
)不是锁某一行,而是锁住索引中两个值之间的“空档”——比如现有记录
id = 5
id = 10
,那
(5, 10)
这个开区间就被锁住了,别人不能往里插
id = 7
id = 8
的新行。它只在
REPEATABLE READ
隔离级别下生效,核心目的就一个:堵住幻读的漏洞

什么操作会触发间隙锁?

不是所有

SELECT ... FOR UPDATE
都会加间隙锁,得看查询条件和索引结构:

范围查询必触发:比如
SELECT * FROM t WHERE id BETWEEN 10 AND 20 FOR UPDATE
,会锁住所有命中范围的记录 + 它们之间的间隙 + 边界外的间隙(如
(5, 10)
(10, 20)
(20, 25)
等值查询但没命中,且字段有**非唯一索引**:比如
WHERE c = 7
,而表里
c
字段只有
5
10
,那就会锁
(5, 10)
唯一索引等值查询且记录存在 → 只加
RECORD LOCK
(行锁),不加间隙锁
READ COMMITTED
级别下,间隙锁被完全禁用,所以不会出现因间隙锁导致的插入阻塞

怎么确认自己被间隙锁卡住了?

常见现象是:事务 A 执行了带锁的范围查询,事务 B 突然发现

INSERT
卡住不动,
SHOW PROCESSLIST
显示
update
insert
状态为
Locked
,但查不到显式锁表语句。这时可以:

查当前锁状态:
SELECT * FROM performance_schema.data_locks;
关注
LOCK_MODE
列:若看到
X,GAP
就是间隙锁,
X,REC_NOT_GAP
是纯行锁,
X
(无后缀)通常是
Next-Key Lock
检查隔离级别:
SELECT @@transaction_isolation;
必须是
REPEATABLE-READ
才可能触发
看执行计划是否走了索引:
EXPLAIN SELECT * FROM t WHERE c BETWEEN 10 AND 20 FOR UPDATE;
如果
type
ALL
(全表扫描),InnoDB 可能退化为锁整个索引范围,甚至升级成表锁

间隙锁容易踩的坑

它不冲突、不互斥,但副作用很强:

多个事务对同一间隙加
Gap Lock
不会阻塞彼此,但只要有一个事务在该间隙内尝试
INSERT
,就会被全部卡住 —— 表面看没锁竞争,实际是“静默阻塞”
主键自增列上做范围查询,间隙可能延伸到
(max_id, +∞)
,导致后续所有
INSERT
都被拦住,尤其在分页写入或批量导入时很隐蔽
业务误以为“没查到数据=没锁”,结果在
WHERE status = 'pending'
(非唯一索引)后直接
INSERT
,却因间隙锁失败,日志里只报超时,不报死锁
升级 MySQL 版本后行为可能变化:8.0.18+ 对唯一索引的等值查询优化更强,但非唯一索引的范围扫描加锁逻辑更激进,建议上线前用
data_locks
实测

真正难的不是理解间隙锁,而是它总在你没意识到的地方悄悄起作用——尤其是当你的查询看似“只读”,却用了

FOR UPDATE
或走的是非唯一索引时。

相关推荐