插入意向锁解析

Insert Intention Lock

Posted by LiuShuo on August 18, 2020

本文主要总结插入意向锁的东东。

概念

插入意向锁的作用是为了提高并发插入的性能,多个事务同时写入不同数据至同一索引范围(区间)内,并不需要等待其他事务完成,不会发生锁等待。

插入意向锁本质上可以看成是一个Gap Lock:

  • 普通的Gap Lock 不允许 在(上一条记录,本记录)范围内插入数据
  • 插入意向锁Gap Lock 允许 在(上一条记录,本记录)范围内插入数据

所以,插入意向锁是表级别的。

插入过程

假设现在tb表(只有一个id字段且为主键)有记录10、11、13、20,现在事务2要插入15。

  • 1.找到小于等于15的记录,即13;
  • 2.找到记录13的下一个记录,即20;
  • 3.判断下一条记录20上是否有锁
    • 3.1.没有,则可以插入
    • 3.2.如果只有Record Lock,则可以插入
    • 3.3.如果有Gap Lock/Next-Key Lock,则无法插入,因为锁定的范围是(13, 20)/(13, 20];但此时当前事务会尝试在该记录上增加Insert Intention Lock(并不会成功,因为其他事务正在持有Gap Lock/Next-Key Lock),但处于waiting状态;当相关的锁持有事务释放了对应的锁时,此时当前事务会 获取到20上的Insert Intention Lock,此时会插入15并成功,此时会持有15记录的Record Lock。在未提交事务前,其他事务是无法写操作15这条记录的,比如delete会失败,因为该记录上已经被当前事务持有了Record Lock。

上面的两个事务很好模拟,事务1执行select * from tb where a <=20 for update,事务2执行insert into tb values(15)即可模拟。

具体可以通过show engine innodb status\G来查看事务的日志,比如在上面的例子中我们会得到:

lock_mode X locks gap before rec insert intention waiting

即事务2想要插入一行数据,需要先获取对应的插入意向锁,但是被事务1阻塞住了因为事务1此时持有Gap Lock,是一个排他锁,导致事务2阻塞。

此时,如果事务1提交事务,事务2会立刻获取到插入意向锁,然后插入记录15成功,并持有该记录的行锁。

如果此时再次打开一个事务3,同样执行插入15的操作,则会阻塞,因为此时事务2持有了15这条记录的行锁:

lock_mode X locks rec but not gap waiting

Reference

本文首次发布于 LiuShuo’s Blog, 转载请保留原文链接.