场景:莫名随机死锁
参考公司业务和B站资源
场景1:下游消费的服务调用组内一个数据接口服务频繁报SQL死锁异常,导致功能修改丢失,造成规模范围内数据异常。
根据日志信息关键词定位为一条sql的死锁问题
我们有一个业务,无法判断它是插入还是更新,所以我们这边的实现是使用insertorupdate来交给数据库来判断执行,然后在高并发情况下会出现死锁的问题
排查:我当时是在服务器上登录mysql,手动提交事务去复现了一下场景,发现事务3自动释放显示发生了死锁。所以我当时去查看这个第一个事务加了什么锁。
自己的建表图:
添加模拟数据:
表锁(意向锁):这个很好理解,我们在插入或者更新的时候肯定是不希望这种DDL语句来影响的,因为这个操作很可以影响事务的执行,所以这把锁的目的是为了阻塞DDL的那些操作
间隙锁:我看它加了两把,这个主要是为了维护索引的那一段范围,是为了防止同时有其他的在这一段间隙里面去插入的事务操作
然后去看第二个事务为什么死锁:去查看这条语句加了什么锁:
间隙锁:它是加了间隙锁,但是这个间隙锁有一个类型,很有意思,是插入意向锁,是和第一个事务的插入的间隙锁有冲突了,事务2加了间隙锁想要插入但是事务1已经加了间隙锁了,所以这个插入意向锁是告诉事务2不要进行插入,要等着事务1完成
然后我去看第三个事务,和事务2一样,也是插入意向锁
现在捋一下,就是事务2等事务1,然后事务3等事务2或者事务1,没有形成闭环
然后我把事务1提交之后,事务2按说就可以执行了,但我们知道在每次insert之前都会去检测有没有其他事务插入的锁,事务2发现事务3也在gap,也就是加了间隙锁,所以事务2的一把间隙锁又被waitting掉了,就是去等待事务3完成,然后事务三没有加锁,也就是事务3也在等事务2的gap,所以形成了死锁
事务1:
事务2:
事务3:
事务1提交,死锁出现:
解决:异常判断
直接insert然后去try catch一下,如果异常是dumplicatekey(唯一性约束)就是update,然后执行update操作