mysql中删除了一条不存在的数据为什么造成死锁了呢?

文章描述了一个生产环境中偶现的添加数据缓慢的bug,经过测试在并发情况下变为必现问题,发现是数据库死锁。问题定位在于删除和重新插入同一ID的数据导致的死锁,即使数据不存在。建议在操作前先检查数据是否存在,以避免类似问题。
摘要由CSDN通过智能技术生成

事故背景

生产环境稳定运行了很久的系统,收到了测试提出的诡异bug,说添加数据很慢,触发了前端的超时,而且还是偶现,这就很郁闷了,生产环境没有出现过,只在测试环境出现过几次,面对这个 bug,我有点头痛,偶现的bug最重要的就是复现问题,然后才能解决,由于在开发环境尝试了很多次之后没有出现,就没有放心上,将bug的状态挂起,备注为持续观察。

直到有一次,测试部门收到需要对接口做性能测试,然后在他们的性能测试中,这个诡异的bug就成了必现问题,在并发情况下,会有大量的线程处于阻塞问题,并且服务器日志显示数据库存在死锁。

如何产生的死锁

添加数据时,存在一张关联表,会对关联表添加对应的数据,但是添加数据之前需要将原来的数据删除,但是删除、插入都有索引,是不可能导致死锁呢?那到底是什么导致数据库死锁了呢?

打开程序的 sql 日志,发现死锁确实存在插入语句中,这就让我更纳闷了,插入的都是新数据,怎么会存在死锁呢?难道是前面的删除导致了插入获取锁失败吗?

问题定位

为了模拟问题,新建一张数据表作为测试。

CREATE TABLE `sys_user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `username` varchar(255) NOT NULL,
  `age` smallint(6) NOT NULL,
  `sex` tinyint(1) DEFAULT NULL,
  `mail` varchar(128) DEFAULT NULL,
  `created_time` datetime DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE,
) ENGINE=InnoDB AUTO_INCREMENT=1796486 DEFAULT CHARSET=utf8mb4;

新建了一张用户表,并且新增两条数据

INSERT INTO `sys`.`sys_user`(`id`, `username`, `age`, `sex`, `mail`, `created_time`) VALUES (1, '李四', 33, 1, '123@qq.com', now());
INSERT INTO `sys`.`sys_user`(`id`, `username`, `age`, `sex`, `mail`, `created_time`) VALUES (2, '张三', 33, 1, '123@qq.com', now());

为了验证我的猜想,这里模拟一下删除和新增的逻辑,开启两个事务A、B,A事务负责插入数据,B事务负责删除数据(为什么插入和删除不在一个事务?只有不同事物才会有可能造成死锁,如果两个事务都是插入,除了插入相同的数据,不然也不可能死锁)。

-- 事务 A
start transaction with consistent snapshot;
INSERT INTO `sys`.`sys_user`(`id`, `username`, `age`, `sex`, `mail`, `created_time`) VALUES (3, '张三', 33, 1, '123@qq.com', '2022-03-31 20:00:34');
commit;
start transaction with consistent snapshot;
delete from sys_user where id = 4;
commit;

同时开始A、B事务,先执事务B的删除操作,删除事务B中 id为4的用户数据,不要提交B事务,然后再插入事务A中 id 为3 的数据(因为两个事务操作的是不同的数据)。

但是结果并没有达到我的预期,我的预期是事务 A 的插入操作会被阻塞,因为程序中就是这样的,但是执行结果却是事务 A 的插入操作并没有被阻塞,而是直接插入成功。

所以得出结论,并不是删除导致插入的死锁,这里也符合mysql的锁原则,id是主键,操作它都是行数,id 3、4互不影响。

这里就让我郁闷了,既然不是删除影响的,那会是什么影响的呢?正当我一筹莫展的时候,我发现,程序中的逻辑是这样的,会经常删除一些重复的数据,比如,我刚添加了id为3的数据,下次我就把他删除了又插入了一条 id 为3的数据,本来是做更新的,开发人员为了偷懒就直接将原来的数据删除然后再次添加。

到这里我可能猜到了问题所在,会不会某个id已经被删除的数据,事务B去删除,然后事务A添加数据的时候正好使用到了这个id,这样两个事务同时操作一条数据,但是删除一条不存在的数据,也会被加锁吗?

尝试修改ssql语句

-- 事务 A
start transaction with consistent snapshot;
INSERT INTO `sys`.`sys_user`(`id`, `username`, `age`, `sex`, `mail`, `created_time`) VALUES (6, '张三', 33, 1, '123@qq.com', now());
commit;
start transaction with consistent snapshot;
delete from sys_user where id = 6;
commit;

终于,死锁出现了,事务A的插入操作发生死锁了,但是id为 6 的数据在数据库并不存在 ,这也就说明了及时在数据库中删除或更新一条不存在的数据,同样会存在锁。

总结

死锁原理
删除和更新同一条数据导致死锁,注意点:即使操作的是数据库不存在的数据,同样会导致锁的存在,程序中因为id的重复使用,所以导致了再多线程的压力下,出现了删除、插入相同的id,而这个id就是程序中不存在的数据(出现这种问题,代码写的有拉跨)。

建议
在更新、删除操作前,请先判断一下是否存在数据在进行,编写代码时一定要严谨,不要抱有侥幸心里。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值