Next-Key Lock在5.7和8.0中加锁范围差异最明显的是范围查询右边界处理:5.7对唯一索引范围查询可能仅锁匹配行,8.0则严格锁至不满足条件的第一个值,且非唯一索引下间隙锁范围更大、更易阻塞。

Next-Key Lock在5.7和8.0中加锁范围差异最明显的地方
不是“有没有Next-Key Lock”,而是“什么时候退化、退化成什么”。MySQL 5.7.24 和 8.0.13 之后的版本,对等值查询的退化逻辑基本一致,但**范围查询的右边界处理变了**:5.7 对
比如 SELECT * FROM ct WHERE id :
- 5.7.22 加的是
(0,10](临键锁,含 id=10 这行) - 8.0.26 加的是
(0,10)(纯间隙锁,不锁 id=10)
这个变化看似小,实则影响插入并发——8.0 下 id=10 可被其他事务插入,5.7 下会被阻塞。验证必须用 performance_schema.data_locks,不能只靠经验猜。
唯一索引等值查询时,记录存在与否决定锁类型
这条规则在 5.7 和 8.0 中都成立,但 8.0 执行更严格,尤其在“记录不存在”时默认加间隙锁,不给“侥幸空间”。
例如表 t 主键 id,当前有 (5),(10),(15):
-
SELECT * FROM t WHERE id = 8 FOR UPDATE:记录不存在 → 8.0 锁(5,10),5.7 可能不加锁或只锁索引项 -
SELECT * FROM t WHERE id = 10 FOR UPDATE:记录存在 → 两者都退化为行锁(只锁 id=10)
业务里常有“先查再插”的逻辑,若查不到就 INSERT,8.0 下这个间隙锁会让后续 INSERT 直接阻塞,5.7 可能悄悄放行。升级后这类逻辑要重测。
非唯一索引上,8.0 更容易锁住更大范围
非唯一索引的等值查询,在 8.0 中加锁更“保守”:它会沿着索引向右遍历直到第一个不满足条件的值,并对中间所有间隙加锁;5.7 有时会提前终止,锁得少。
比如索引 c 上有值 0,5,10,15,执行 SELECT * FROM t WHERE c = 5 FOR UPDATE:
- 5.7 可能只锁
(0,5]和(5,10) - 8.0 明确加
(0,5](next-key) +(5,10)(gap),且因需检查重复性,还会访问并锁住c=10对应的主键行(如果该行在聚簇索引中被扫描到)
这意味着:同样一条 UPDATE 语句,在 8.0 下可能阻塞更多并发 INSERT,尤其当 c 值分布密集时。别只看 EXPLAIN,得跑 SHOW ENGINE INNODB STATUS\G 看 LOCK WAIT 段落。
隐式类型转换让 8.0 的锁范围剧增,且不可逆
字段是 VARCHAR,却写 WHERE mobile = 13800138000,这种写法在 5.7 可能还走索引、只锁几行;8.0 的优化器更早判定“无法用索引”,直接全表扫描 + 每行加 X 锁(或临键锁),效果等同于锁表。
这不是 bug,是行为收敛:8.0 不再容忍模糊匹配带来的不确定性。关键点:
- 只要出现隐式转换,
EXPLAIN的key字段大概率是NULL,type是ALL - 此时
performance_schema.data_locks会显示大量RECORD类型锁,INDEX_NAME是PRIMARY,LOCK_DATA覆盖全部主键值 - 8.0 已移除
innodb_locks_unsafe_for_binlog,没法靠参数“关掉”这部分锁
最容易被忽略的是:这类问题在单事务、低并发下完全无感,只有压测时才暴露为慢 SQL + 大量锁等待。上线前必须扫一遍慢日志里的 WHERE 条件,逐条核对字段类型。


















