千家信息网

innodb next-key lock引发的死锁现象分析

发表于:2024-10-16 作者:千家信息网编辑
千家信息网最后更新 2024年10月16日,这个例子是我在网上看到的,我分析了很久才弄明白锁产生的具体过程。数据库的事务隔离级别是RR。建测试表:CREATE TABLE `LockTest` (`order_id` varchar(20) N
千家信息网最后更新 2024年10月16日innodb next-key lock引发的死锁现象分析这个例子是我在网上看到的,我分析了很久才弄明白锁产生的具体过程。

数据库的事务隔离级别是RR。

建测试表:
CREATE TABLE `LockTest` (
`order_id` varchar(20) NOT NULL,
`id` bigint(20) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`),
KEY `idx_order_id` (`order_id`)
) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8;

测试步骤:
事务1事务2

begin

delete from LockTest where order_id = 'D20'

begin

delete from LockTest where order_id = 'D19'

insert into LockTest (order_id) values ('D20')

insert into LockTest (order_id) values ('D19')

commit

commit


测试结果:
事务1 执行到insert语句会block住,事务2执行insert语句会提示死锁错误。

原因分析:
1、首先看测试表的建表语句,id是主键索引,同时该主键是自增主键。order_id是普通索引。
2、事务1执行delete from LockTest where order_id = 'D20';语句时,由于数据库的隔离级别是RR,因此此时事务1在主键id上获得了一个next-key lock,这个锁的范围是[16, +∞)。
这个16就来自于AUTO_INCREMENT=16,因为LockTest目前是张空表。
3、同理,事务2执行delete from LockTest where order_id = 'D19';语句时,由于数据库的隔离级别是RR,事务2在主键id上也获得了一个next-key lock,这个锁的范围是[16, +∞)。
也就是说此时,事务1和事务2获得的锁是一样的。
4、事务1继续执行insert into LockTest (order_id) values ('D20');语句,这个时候由于该语句企图往LockTest表insert一行id=16,order_id=D20的数据,
但是由于在事务2的delete语句中,主键id上已经有了一个范围为[16, +∞)的锁,导致事务1此时想插入数据插不进去,被阻塞了。
5、继续事务2的插入语句insert into LockTest (order_id) values ('D19'); 该插入语句同样也想LockTest表insert一行id=16,order_id=D19的数据,
但是由于由于在事务1的delete语句中,主键id上已经有了一个范围为[16, +∞)的锁,导致事务2此时想插入数据插不进去,被阻塞了。
此时,可以发现,事务1和事务2的锁是互相持有,互相等待的。所以innodb判断该事务遇到了死锁,直接将事务2进行了回滚。然后回头去看事务1,insert into LockTest (order_id) values ('D20');被成功执行。

如果你将数据库的事务隔离级别修改为RC,上述事务会各自成功运行,不会互相影响。






0