bigywater.tech
MySQL / InnoDB · 53

Next-Key Lock

用两个都落在id=20附近、但一个匹配真实数据一个不匹配的查询,看Next-Key Lock和纯Gap Lock在锁定记录本身这件事上真实有什么不同。

X,GAP
不匹配真实数据时,只锁间隙不锁记录
X(Next-Key)
匹配到真实数据时,间隙和记录一起锁

做的事:两个查询,同样落在id=20附近,一个匹配真实数据一个不匹配

代码-- 查询①:范围内没有真实存在的行(accounts的id是10,20,30...) SELECT * FROM accounts WHERE id BETWEEN 12 AND 18 FOR UPDATE; -- 查询②:范围内真实匹配到id=20这一行 SELECT * FROM accounts WHERE id BETWEEN 10 AND 30 FOR UPDATE;

验证:真实的锁标记不一样——一个是纯GAP,一个是Next-Key

实测 · 本机performance_schema.data_locks真实输出
查询① BETWEEN 12 AND 18(没匹配到行):
  RECORD  X,GAP   lock_data=20   ← 纯间隙锁,落在记录20上但只锁间隙,不锁记录本身

查询② BETWEEN 10 AND 30(匹配到id=20这一行):
  RECORD  X       lock_data=20   ← Next-Key锁:间隙 + 记录20本身,都锁

两条锁记录的lock_data真实都是20,但锁的类型不一样:查询①因为范围内没有真实存在的行,InnoDB只能把锁挂在"下一条记录"(id=20)上,但只锁它前面的间隙(12,18)所在的那段区间,不锁记录20本身,标记成X,GAP;查询②因为id=20真实落在了BETWEEN范围内,InnoDB给它的是Next-Key锁——间隙加记录本身一起锁,没有GAP这个单独标记(默认的X锁本身就隐含"间隙+记录")。

验证:真实测试对记录20本身的更新,两种锁的效果完全不同

实测 · 本机执行输出(并发UPDATE accounts SET balance=balance+1 WHERE id=20)
持有纯GAP锁(查询①)时并发更新id=20:   真实立即成功
持有Next-Key锁(查询②)时并发更新id=20: ERROR 1205 Lock wait timeout   ← 真实被挡住
这就是Next-Key Lock和纯Gap Lock最真实的区别:纯Gap Lock只保证"这个间隙里不会真实冒出新行",它并不关心边界记录本身的状态,所以别的事务真实可以正常更新id=20这一行;Next-Key Lock是"间隙锁+记录锁"的合体,它既要保证间隙里不会冒出新行,也要保证被锁的记录本身不会被别的事务真实修改——这正是REPEATABLE READ下InnoDB默认的加锁方式:普通的WHERE 条件 FOR UPDATE,只要真实匹配到了具体的行,锁住的就是Next-Key Lock,而不是单纯的记录锁或者单纯的间隙锁。

分步演示:是否匹配到真实的行,决定了锁的种类

每一步都是本文上面用performance_schema.data_locks和真实并发更新跑出来的原始结果。

同样落在id=20,纯GAP锁和Next-Key锁真实的分野
这一步新验证的锁类型

    本文用到的工具

    • 本机 MySQL 26.7.0(InnoDB),accounts表,两组对比查询(承接上一篇《Gap Lock》)
    • performance_schema.data_locks:区分lock_mode是X(Next-Key)还是X,GAP(纯间隙)
    京ICP备2026067502号-1京公网安备11010502063487号