用两个都落在id=20附近、但一个匹配真实数据一个不匹配的查询,看Next-Key Lock和纯Gap Lock在锁定记录本身这件事上真实有什么不同。
代码-- 查询①:范围内没有真实存在的行(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;
查询① 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锁本身就隐含"间隙+记录")。
持有纯GAP锁(查询①)时并发更新id=20: 真实立即成功 持有Next-Key锁(查询②)时并发更新id=20: ERROR 1205 Lock wait timeout ← 真实被挡住
每一步都是本文上面用performance_schema.data_locks和真实并发更新跑出来的原始结果。
performance_schema.data_locks:区分lock_mode是X(Next-Key)还是X,GAP(纯间隙)