between更新加锁包含右边界是因为innodb在rr隔离级别下默认使用临键锁,锁定的是索引中真实存在的键值推导出的左开右闭区间(left, right],其边界由索引树结构动态决定,与sql字面值无关。

BETWEEN 更新加锁包含右边界,是因为 InnoDB 在 REPEATABLE READ 隔离级别下默认使用临键锁(Next-Key Lock),它锁定的是「索引中真实存在的键值推导出的左开右闭区间 (left, right]」,不是 SQL 写的字面范围。
临键锁的右边界由索引数据动态决定,不是 SQL 字面值
执行 UPDATE t SET x=1 WHERE id BETWEEN 15 AND 25 时,InnoDB 不会锁 (15, 25] 这个数值区间,而是:
- 向左找 ≤15 的最大已存在主键值,比如是
10 - 向右找 ≥25 的最小已存在主键值,比如是
30 - 最终加锁范围是
(10, 30]—— 即间隙(10, 30)+ 记录30
如果表里根本没有 30,而下一个是 40,那右边界就变成 40;如果 10 也不存在,左边界可能退到负无穷。这个行为和 BETWEEN 写的数字无关,只取决于索引树结构。
唯一索引和普通索引对右边界锁定的影响不同
主键或唯一索引上 BETWEEN 虽然命中单行,仍按临键锁处理:
-
WHERE pk BETWEEN 100 AND 100→ 锁(prev_pk, 100],不是单行 - 普通索引(如
age)有重复值时,先锁age索引上的间隙,再对每条匹配的主键回表加记录锁 - 这意味着普通索引上
BETWEEN的右边界可能触发多行记录锁,甚至跨多个主键值
DATETIME 字段用 BETWEEN 更新时,右边界隐式补零会扩大锁范围
写 WHERE create_time BETWEEN '2026-06-01' AND '2026-06-30',MySQL 自动转成 '2026-06-01 00:00:00' AND '2026-06-30 00:00:00',导致:
- 实际锁的右边界是
'2026-06-30 00:00:00'对应的索引位置 - 哪怕当天只有 1 条记录在
00:00:00,也会锁住该点左侧所有间隙,包括'2026-06-29 23:59:59'之前的所有空隙 - 用
create_time >= '2026-06-01' AND create_time 可避免这种意外锁扩散
容易被忽略的关键点:锁范围不随 WHERE 条件“收紧”而缩小
即使你把 BETWEEN 写得很窄,比如 id BETWEEN 100 AND 100,只要索引中 100 左侧最近的值是 90、右侧是 110,InnoDB 就照锁 (90, 110]。这个逻辑藏在 B+ 树查找路径里,没法靠肉眼判断,必须查 INFORMATION_SCHEMA.INNODB_TRX 和 INNODB_LOCKS 才能确认实际锁了哪些间隙和记录。











