
点击上方蓝字关注我!
背景
新项目准备上线,测试在测试功能时,发现点击按钮后页面就卡住不动了,开始以为是网络问题,但是这个页面卡住百分之百复现。查看后台日志,发现在执行更新语句的时候被锁住了。
通过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