码迷,mamicode.com
首页 > 数据库 > 详细

一次Mysql下批量更新造成的死锁案例分析

时间:2019-12-19 21:15:19      阅读:120      评论:0      收藏:0      [点我收藏+]

标签:可重复   http   tno   normal   其他   img   并发   行锁   异常   

  最近,公司现网的业务中出现上图所示的死锁异常,沿着问题分析,发现这个问题涉及很多数据库的基础知识。

 

背景:

  使用数据库:Mysql

  涉及表格:t_invest

  数据库隔离级别:可重复读(Repeatable Read)

  死锁场景:saveRepaymentInfo事务的A()方法对t_invest表执行如下update操作:

    <update id = "A" parameterType = "java.util.List">

      <foreach collecton = "list" item = "item" separator = ";">

        update t_invest

          set status = 1,

          end_date = #{item.endDate},

          period = #{item.periods},

          update_time = now()

          where invest_no = #{item.invest_no}

      </foreach>

      </update>

      invest_no字段为设置了唯一索引

异常信息:

技术图片

 

死锁分析:

通过异常日志跟踪定位到业务代码,发现死锁出现在syncRedeemApply事务。

事务syncRedeemApply会进行t_invest表status的批量更新,批次最大数量为1000条。t_invest表的事务隔离级别为“可重复读”,在该隔离级别下,每次执行更新操作时会对索引加行锁,这个事务不存在多线程并发访问的情况,推断不是因为多程序并发操作造成的死锁。

通过分析业务功能发现,其他分布式业务模块在saveRepaymentInfo事务进行t_invest表的status的批量更新的同时,另外有一个updateExitStatusByBatch事务同时也在invest表进行批量更新,而且都是利用investno索引进行批量update,问题就出现在这里!

updateExitStatusByBatch事务进行批量更新的方法假设是B(),其批量更新语句为:

  <update id = "B" parameterType = "java.util.List">

    <foreach collecton = "list" item = "item" separator = ";">

      update t_invest

        set status = 1,

        end_date = #{item.endDate},

        period = #{item.periods},

        update_time = now()

        where invest_no = #{item.invest_no}

    </foreach>

  </update>

(其实两个事务的方法A与B的sql是一样的)

 死锁是因为两个或两个以上的线程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。

 如下图所示:

技术图片

t_fixin_invest的事务隔离级别为“可重复读”,每次执行更新操作时会对索引加行锁,在一个事务内批量修改如果没有全部修改完,索引是不会被放开的,亦即索引上的行锁不会被放开,所以当两个事务中同时进行更新时,如果有重复数据,有可能出现互相等待,从而爆死锁。

如上图,syncRedeemApply事务锁住i1、i3、i5,updateExitStatusByBatch事务锁住i2、i4、i6,syncRedeemApply事务执行到i2的update,等待updateExitStatusByBatch事务释放i2的锁,updateExitStatusByBatch事务执行到i1的updat,等待syncRedeemApply事务释放i1的锁,如此,两个事务互相等待,造成死锁。

我的解决办法是避免这两个事务操作使用相同的索引进行更新,给表t_fixin_invest增加一个联合索引(investno,pno),然后其中一个批量操作使用这个索引更新,可避免死锁!

 

技术图片

一次Mysql下批量更新造成的死锁案例分析

标签:可重复   http   tno   normal   其他   img   并发   行锁   异常   

原文地址:https://www.cnblogs.com/pufeng/p/12069835.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!