快照读的可重复读是依赖MVCC实现的,而一致性读(也称作当前读)的可重复读还需要依靠间隙锁gap lock。

环境准备

  • 克隆项目https://github.com/cafewang/playground
  • 确认可以从命令行访问docker
  • 在mysql-test目录下的运行sh ./start_server启动mysql数据库
  • 运行PrimaryKeyQueryTest和UniqueKeyQueryTest中的测试,注意jdk版本最低为11

mysql版本是8.0.41,不同版本下的锁表现可能不一致。
初始时user命名空间下有表employee,有主键id和带唯一索引的字段no,name默认为空串。
初始数据如下

id no name
5 105  
10 110  
15 115  
20 120  

主键等值查询

命中

查询id=10的记录,在另一个事务中更新id=10的记录,更新事务被阻塞。
在新事务中插入id=9和id=12都不受影响。

未命中

查询id=12,在新事务中插入id=11会阻塞,更新id=10和id=15不受影响,间隙锁范围为(10,15)。

可见未命中的主键查询,会在主键之间加上间隙锁,防止查询期间其他事务插入数据。

主键范围查询

单点

查询范围为[10,11),即id >= 10 and id < 11。在新事务中

  • id=9可以插入
  • id=10无法更新
  • id=12无法插入
  • id=15可以更新

可以看到id < 11给(10, 15)加上了间隙锁,但是没给15加上行锁。
可以推断出,间隙锁会加到不满足查询条件的下一个主键,对于id < 11就是15,但是不会在下一个主键上加锁。

跨主键

查询id范围为[4, 12),在新事务中

  • id=1不可插入
  • id=5不可更新
  • id=6不可插入
  • id=10不可更新
  • id=12不可插入
  • id=15可以更新

实际上,此时的锁有

  • 间隙锁:(-∞, 5), (5, 10), (10, 15)
  • 行锁:5, 10

上限

查询id范围[16, 21),在新事务中

  • id=15可更新
  • id=16不可插入
  • id=20不可更新
  • id=24不可插入

此时间隙锁有(15, 20),(20, +∞),行锁有20。
总结一下,一致性读在主键上范围查询,范围两端落到存在的主键之间都会加上间隙锁,包括不等于的情况,
比如id < 11,会在(10, 15)上加间隙锁。

唯一索引等值查询

带唯一索引的字段和主键的加锁逻辑有所不同。

命中

查询no=110,会给唯一索引=110加行锁,以及对应主键索引10加行锁,不会影响其他记录的插入和更新。

未命中

查询no=112,在新事务中

  • 可以通过no=110更新name字段update employee set name = ? where no=110
  • 不能插入no=114的记录
  • 可以通过no=115更新

可以看出,在等值查询上,唯一索引和主键索引并没有什么区别。

唯一索引范围查询

单点

查询no范围为[110, 111)的记录,在新事务中

  • 可以通过no=105更新
  • 不能插入no=109
  • 不能通过no=110更新
  • 不能插入no=112
  • 不能通过no=115更新
  • 可以插入no=116

可以看出,和主键不同,唯一索引的一致性读会在查询范围两端都加上next-key锁,即间隙锁右端加上行锁。
no >= 110加上(105, 110]的锁,no < 111加上了(110, 115]的锁,
所以整体构成了(105, 115]的左开右闭区间。

跨端点

以no范围[105, 112)查询,在新事务中

  • 不能插入no=101
  • 不能插入no=108
  • 不能插入no=114
  • 不能通过no=115更新
  • 可以插入no=116

next-key锁有(-∞, 105]、(105, 110]、(110, 115]。

上限

以no范围[115, 121)查询,在新事务中

  • 可以通过no=110更新
  • 不能插入no=114
  • 不能插入no=117
  • 不能插入no=130

next-key锁有(110, 115]、(115, 120]、(120, +∞)。 总结一下,唯一索引的加锁方式和主键并不一样:

  • 使用next-key锁,会锁住查询范围外的下一个记录,如no < 112,会锁住no=115的记录
  • 只要是范围中的记录都会加上next-key锁,如no >= 115,会加上(110, 115]的锁

Categories: ,

Updated: