用户国营 发表于 2022-12-1 18:53:21

MySQL 间隙锁导致的死锁场景分析

实际业务场景

在我们使用mysql的时候,如果不注意间隙锁容易引起死锁,最近分析一个业务场景就是间隙锁导致的死锁,业务抽象如下:
系统有一个批量新增业务资源的功能,实现逻辑如下(businnessid为非唯一索引):
update 业务表 set isdeleted=1 where bussinessid=123;
insert into 业务表
在并发场景下,以上逻辑产生了死锁。
以下为死锁具体分析以及还原死锁产生过程,最后给出解决方案。
创建一张表

CREATE TABLE `lock_demo` (
`id` INT NOT NULL AUTO_INCREMENT,
`index` INT NOT NULL,
`name` VARCHAR(50) NULL DEFAULT NULL,
PRIMARY KEY (`id`),
INDEX `index` (`index`)
)
COLLATE='utf8mb4_0900_ai_ci'
ENGINE=InnoDB
;
死锁演示

事务一
事务二
update lock_demo set name='a' where index=1;
 
 
update lock_demo set name='a' where index=2; 未卡住,间隙锁之间不冲突
insert into lock_demo(id,`index`,name)values(1,1,'1');
卡住,被事务二的间隙锁锁住
 
 
insert into lock_demo(id,`index`,name)values(2,2,'2');
检测到死锁
 具体步骤

步骤一

事务一
先更新一条记录(不存在),不提交。
 https://img2023.cnblogs.com/blog/597136/202212/597136-20221201145024276-635153565.png
 
 
 
步骤二

事务二
更新一条记录(不存在),不提交。
 https://img2023.cnblogs.com/blog/597136/202212/597136-20221201145031430-1089863839.png
 
 
 
步骤三

事务一,执行一条新增语句,被事务二的间隙锁锁住
 https://img2023.cnblogs.com/blog/597136/202212/597136-20221201145037121-449387590.png
 
 
 
步骤四

事务二,执行一条新增语句,被事务一的间隙锁锁住,系统检测到死锁
 https://img2023.cnblogs.com/blog/597136/202212/597136-20221201145046225-1837039659.png
解决方案

对于以上的业务,可以改写sql避免死锁
select id from 业务表  where bussinessid=123;
如果存在,
update 业务表 set isdeleted=1 where id in (xxx);
insert into 业务表
 

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: MySQL 间隙锁导致的死锁场景分析