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

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

点击上方蓝字关注我!



背景

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

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

通过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

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

原文始发于微信公众号(Lvshen的技术小屋):什么,同事写的代码导致数据库死锁了

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/262295.html

(0)
Java朝阳的头像Java朝阳

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!