next-key locking Update不存在的行数据 二级索锁和主键锁

在centos7 mysql 5.7.27里, 默认的事务隔离级别是repeatable read, 默认使用next-key locking. 

我看到很多资料和文章都只解释了等值查询for update加X锁的情况, 那如果是大于或者大于等于查询呢?

以下是对next-key locking的一些理解, 我就不写太多概念的东西了, 难理解. 

create table n(a int, b varchar(10), c int, primary key(a), key(c));
insert into n select 1, '1', 1;
insert into n select 10, '10', 10;
insert into n select 20, '20', 20;
insert into n select 30, '30', 30;
insert into n select 50, '50', 50;

autocommit是连接级别的一个变量, 默认是打开的:

next-key locking
Update不存在的行数据
二级索锁和主键锁

先把它关掉:

set @@autocommit=0;

 next-key locking
Update不存在的行数据
二级索锁和主键锁

如下语句会锁住[30, +无穷大):

select c from n where c>30 for update;

如下语句会锁住[20, 30]和[30, +无穷大), 其实就是[20, +无穷大):

select c from n where c>=30 for update;

其实是锁定前一个索引值到30之间, 以及30到+无穷大之间, 也包括30本身.

这些锁定都是在c索引上, c索引是建立在c列上的一个允许重复的普通索引.

会使用用next key locking.

表结构和数据如下:

next-key locking
Update不存在的行数据
二级索锁和主键锁

 orderid和productid都是普通索引, 先开启事务A, 对于以下语句, 在repeatable read级别下会用S锁住进行next key locking:

begin;
update
sorderdetail set description='333' where productid=100;

也就是[90, +无穷大)

此时如果事务B执行类似的语句:

begin;
update
sorderdetail set description='333' where productid=101;

如此是可以执行成功的, 因为也只会在[90, +无穷大)加S锁.

接下来, 事务A执行如下语句:

insert into sorderdetail(orderid, productid, description) values(90,90," 90 test description");

将会卡住, 因为insert语句需要加X锁, 对于以上语句, 是需要对90这个productid索引加X锁, 但是由于上面事务B持有[90, +无穷大)的S锁以及X锁的排它性, 事务A此时在等事务B释放[90, +无穷大)的S锁, 所以无法执行.

事务A在等待的过程中, 事务B如果再执行如下语句:

insert into sorderdetail(orderid, productid, description) values(92,92," 92 test description");

类似的, 这条insert需要对92加X锁, 但由于事务A也持有了[90, +无穷大)的S锁以及S锁与X锁不兼容, 所以, 事务B在等事务A释放[90, +无穷大)上的S锁, 而事务A也在等事务B释放[90, +无穷大)的S锁, 形成死锁.

此时innodb引擎会检测到这里形成了死锁, 会自动把事务B回滚, 让事务A执行下去.

如果不是尝试插入100而是尝试插入70, 那么会用next key locking的区间就是[51, 75), 原理类似.

二级索锁和主键锁

现在有一个奇怪的问题:

事务A尝试update一条不存在的记录76, 按理说此时会用next key locking在[75, 80)上加S锁, 这么一来, 事务B应该是既无法insert 75, 也无法update 75的记录才对, 可是事实是, 事务B还是可以update 75, 但是insert会被要求等待锁. 为什么事务B可以update 75呢??? 稍后回来思考~~

注意这里的事务A加的S锁区间[75, 80)只存在于productid这个索引上, 而在主键索引上没有加任何锁. 事务B如果要update 75的话, 所加S锁在75上,所加X锁在id=22的主键索引上, id=22的主键索引上没有任何锁, 所以事务B可以执行这句update语句. 而insert? 是又需要在product索引上75加X锁的, 所以和事务A的S锁区间[75, 80)冲突, 无法执行.

以上.