SQL中BETWEEN查询加FOR UPDATE为何会在5之后产生间隙锁?
为什么范围加锁后,超出条件的A=6也被阻塞?
问题还原
表结构与初始数据
CREATE TABLE justpk ( A INT, B INT, PRIMARY KEY (A) ); INSERT INTO justpk (A, B) VALUES (1, 1), (4, 1), (5, 1);
查询结果:
+---+------+ | a | b | +---+------+ | 1 | 1 | | 4 | 1 | | 5 | 1 | +---+------+
会话操作
会话1执行加锁查询:
begin; SELECT * FROM justpk WHERE A BETWEEN 1 AND 5 FOR UPDATE;
另一个会话的操作结果:
INSERT INTO lockingreads.justpk (A, B) VALUES (0, 1);→ 正常执行UPDATE lockingreads.justpk SET B = 2 WHERE A = 1;→ 被阻塞INSERT INTO lockingreads.justpk (A, B) VALUES (2, 1);→ 被阻塞INSERT INTO lockingreads.justpk (A, B) VALUES (3, 1);→ 被阻塞UPDATE lockingreads.justpk SET B = 2 WHERE A = 4;→ 被阻塞UPDATE lockingreads.justpk SET B = 2 WHERE A = 5;→ 被阻塞INSERT INTO lockingreads.justpk (A, B) VALUES (6, 1);→ 被阻塞(疑问:A=6不在1-5范围内,为何被锁?)
原因解析
这是InnoDB在可重复读(RR)隔离级别下的**临键锁(Next-Key Lock)**特性导致的,核心是为了避免幻读:
- 临键锁的本质:临键锁是行锁+间隙锁的组合,当你用范围条件(比如
BETWEEN、>、<)执行加锁查询时,InnoDB不仅会锁住符合条件的行,还会锁住这些行之间的间隙,以及最后一个匹配行之后到下一个主键值之间的间隙。 - 为什么A=6被锁?:你的表中最后一个匹配行是
A=5,而表中没有比5更大的主键值,所以InnoDB会把间隙锁延伸到(5, +∞)的范围——也就是所有大于5的主键值都无法插入。这是因为InnoDB要确保在会话1的事务结束前,不会有新行插入到这个“末尾间隙”中,彻底杜绝幻读的可能。 - 为什么A=0可以插入?:第一个匹配行是
A=1,但你的查询条件是A >=1,InnoDB不会对(-∞,1)的间隙加锁,因为这个区间的行根本不满足查询条件,插入A=0不会影响会话1的查询结果,所以允许执行。
简单说:RR级别下,范围加锁会锁到最后一个匹配行的“后面所有空位”,哪怕这些空位超出了你写的查询范围,目的就是防止新行插进来搞出幻读。
内容的提问来源于stack exchange,提问作者user19481364
相关产品推荐
相关产品推荐

