对《insert 加锁与死锁分析》一文中 gap lock 栗子做个补充解释,原文是这样的:
如果是在 REPEATABLE-READ,除以上所说的唯一约束冲突外,gap lock 的存在是这样的:
普通索引(非唯一索引)的S/X Lock,都带 gap 属性,会锁住记录以及前1条记录到后1条记录之间的间隙,比如有[4,6,8]记录,delete 6 时除了会对 6 加 X lock,也会锁住(4,6),(6,8)这个两个间隙。
在一些情况下,会发现不仅仅(4,6)的间隙被锁了,记录4好像也被锁了,好像是对[4,6) 这个左闭右开的区间间隙加锁。
真的是这样吗?如何解释呢?
实际上记录 4 是不会加锁的,可以通过 delete 4 不被阻塞来验证。但是确实 insert 4 会被阻塞,而 insert 8 不会被阻塞,会造成记录 4 会加锁的误解。实际上这里还是 gap 锁导致的,而并不是记录锁。
至于为什么 insert 4 会阻塞,而 insert 8 不会被阻塞,与主键值有关系:
如果表没有主键,会有个 row_id 隐藏主键(递增),则 insert 4 实际为对索引插入记录 (4,n),n为主键值,delete 6 实际是对索引删除记录 (6,m),m也为主键值,n 一定会大于 m(因为递增),所以(4,n)在被锁的间隙中。而(8,n)在锁的间隙外,所以不会被阻塞。
如果有主键,则:
- 如果 n < m,insert(4,n)不会被阻塞;
- 如果 n > m,insert(4,n)会被阻塞。