美文网首页
delete+insert 引发的死锁问题

delete+insert 引发的死锁问题

作者: 卡萨布兰卡ginger | 来源:发表于2020-07-06 14:18 被阅读0次

    问题

    当上层业务会短时间内调用两次接口,导致线上报错死锁,报错信息如下:

    屏幕快照 2020-07-06 上午10.56.05.png

    死锁日志:


    屏幕快照 2020-07-06 下午2.11.23.png

    db结构

    唯一索引 userid+userKey
    普通索引 userkey+uservalue

    场景还原

    updateUniqueClientIdAndUserValue方法里面,一共有三个db操作:①根据userId和userKeys、userValues查询②根据userkey和value删除数据③批量保存或更新key-value信息

    可直接定位到删除+insert这部操作的问题;

    下面的表格是两个事务中的db操作:

    (红色字体为db提示)

    ![ 屏幕快照 2020-07-06 下午2.14.12.png

    ]


    屏幕快照 2020-07-06 下午2.14.12.png

    至此,死锁问题复现

    分析原因

    首先,死锁为什么会产生?死锁的产生需要相互等待资源,相互等待的资源是什么?

    了解这个之前,先了解一下delete操作的时候需要获取什么锁。

    屏幕快照 2020-07-06 下午2.14.17.png

    以及锁之间的兼容关系

    屏幕快照 2020-07-06 下午2.14.22.png image.png

    由此,可以分析出,结合死锁日志,事务1执行delete操作的时候,获取了索引区间(1086,415097555)的gap锁;

    事务2执行的时候,也获取了索引区间(1086,415097555)的gap锁;由于gap锁之间相互兼容,到这一步为止是正常执行的;

    事务1insert的时候,需要先获取一个插入意向锁(insert intention),由于官方文档解释,插入意向锁被认为是一种gap锁,这两个锁之间不兼容,事务1需要等待事务2释放索引区间(1086,415097555)的gap锁,此时db在等待事务2释放资源,也没有产生死锁;当事务2也执行insert的时候,事务2也需要获取插入意向锁,也要等待事务1释放索引区间(1086,415097555)的gap锁,事务2发生死锁,事务回滚,gap锁资源释放;事务1获取到这个锁,执行成功。

    解决办法

    可以看出来,主要是因为删除了一条不存在的数据导致的,再删除之前先查询,再删除;

    我们可以来走一下先查询再删除的场景:

    事务1删除一条数据,受影响1行,或者到了这条记录的锁以及这个索引之前的区间gap锁;事务2也要删除这条数据,需要先获得这条记录的行锁,等待事务1执行insert之后事务提交,释放锁资源;

    思考

    1. 线上出现这个死锁的问题时,线下并没有马上复现,因为删除的数据是存在;只复现除了等待锁资源超时
    2. 死锁日志确实也提到了当insert操作时,获取的(1086,415097555)的插入意向锁那边有问题

    相关文章

      网友评论

          本文标题:delete+insert 引发的死锁问题

          本文链接:https://www.haomeiwen.com/subject/ptbwqktx.html