什么,同事写的代码导致数据库死锁了

背景

新项目准备上线,测试在测试功能时,发现点击按钮后页面就卡住不动了,开始以为是网络问题,但是这个页面卡住百分之百复现。查看后台日志,发现在执行更新语句的时候被锁住了。

图片

通过sql查询

select * from information_schema.innodb_trx;

我们发现是更新表sys_sn_rule导致的,那么我们理一下代码,看看为什么会出现LOCK_WAIT。

原因排查

通过查看接口调用,我们定位到一个方法上,这里我将方法简化。简化后的代码如下:

@Override
@Transactional(rollbackFor = Exception.class)
public Result funA() {
    //更新表table1;
    funB();
    ...
}

public void funB() {
   //更新表table1;
   ....
}

问题就出在,funA调用了funB,而两个方法同时操作了表table1上的同一条数据。这里会有两个事务,在更新数据时,会产生两个事务都在互相等待对方关闭事务,从而到时死锁。我们来作图说明下:

图片

死锁图解

如上图,执行funA时,会执行更新表table1,更新表前会开启事务A,更新表时会给这行数据上锁(为了保护数据的一致性)。接下来调用funB,开启事务B,更新表table1,因为表table1的这行已经锁住了,所以事务B中需要等锁释放才能继续执行。但是事务A要想关闭,需要等funA执行完才能关闭。而funA中调用了funB,funB要等待table1释放锁才能执行完。这样就导致了死循环。

关于数据库加锁的知识,可以看看这篇文章:

解决死锁之路 - 学习事务与隔离级别 - aneasystone's blog

(https://www.aneasystone.com/archives/2017/10/solving-dead-locks-one.html)

解决办法

我们可以采用多线程解决:

@Override
@Transactional(rollbackFor = Exception.class)
public Result funA() {
    //更新表table1;
    taskExecutor.execute(() -> {
        try {
            Thread.sleep(5 * 1000);
            funB();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    });
    ...
}

public void funB() {
   //更新表table1;
   ....
}

采用多线程,两个事务就分别在两个不同的线程里面了,就不会出现循环等待的情况。

图片

死锁图解-多线程

代码修改后,测试顺利通过。

往期推荐

扫码二维码,获取更多精彩。或微信搜Lvshen_9,可后台回复获取资料

  1. 回复"java" 获取java电子书;

  2. 回复"python"获取python电子书;

  3. 回复"算法"获取算法电子书;

  4. 回复"大数据"获取大数据电子书;

  5. 回复"spring"获取SpringBoot的学习视频。

  6. 回复"面试"获取一线大厂面试资料

  7. 回复"进阶之路"获取Java进阶之路的思维导图

  8. 回复"手册"获取阿里巴巴Java开发手册(嵩山终极版)

  9. 回复"总结"获取Java后端面试经验总结PDF版

  10. 回复"Redis"获取Redis命令手册,和Redis专项面试习题(PDF)

  11. 回复"并发导图"获取Java并发编程思维导图(xmind终极版)

另:点击【我的福利】有更多惊喜哦。

猜你喜欢

转载自blog.csdn.net/wujialv/article/details/113392118
今日推荐