一次线上死锁问题

这两天遇到了一个死锁的问题,下面是errorLog打的报错信息

经过网上资料查询,原因为:Spring 事务嵌套造成死锁。

该异常为一个service中调用了另一个service,两个service对同一表进行操作,造成事务嵌套,从而死锁。

解决办法:在当前方法前加入@Transactional(propagation=Propagation.SUPPORTS)


这个参数的作用是:支持当前事务,如果当前没有事务,就以非事务方式执行。

如果其他bean调用这个方法,在其他bean中声明了事务,那就用事务,如果其他bean中没有声明事务,那就不用事务。?这个不是很理解


以上都是网上查询该报错对应实际应用的解决方案,由于这台服务器的mysql版本是5.1的,information_schema还没有加入innodb事务相关的表查询,升级到5.6版本以后可以查询INNODB_LOCKS、INNODB_TRX、INNODB_LOCK_WAITS;

于是我只能查mysql的innodb运行状态,

语句是:SHOW ENGINE INNODB STATUS\G;

下面是死锁的部分信息 可以看到player_general_military这个表产生了死锁 被两个不同的线程持有 一个是增加exp 一个是重置evoke_buff


一次线上死锁问题_第1张图片

我们知道mysql 事务具有acid属性,分别是代表原子性,隔离性,一致性,持久性。

根据原子性可以知道事务操作是不可再分的,每个事务要么全部成功要么全部失败,通过隔离性可以知道:事务之间不会相互影响。

但是本次为什么还出现了并发事务出现了死锁问题呢?

根据死锁日志可以提取几个有用信息:


一次线上死锁问题_第2张图片

首先mysql默认的隔离级别是可重复读,事务未提交之前总是读到相同的记录,该隔离级别就是为了避免读已提交出现的幻读现象,采用的是GAP间隙锁实现。

根据上表可以得到信息,两个事务都未提交,或者说行锁锁定的记录之外没有其他事务提交的与之有关的记录,所以都未用到gap锁

根据日志可以发现update语句会持有排他锁(共享锁是in share mode)。

事务1等待排他锁,事务2持有事务1的排他锁,并且等待排他锁。这样就能死锁了??为什么事务1没有持有事务2的共享锁

mysql官方有个bug帖:https://bugs.mysql.com/bug.php?id=77209

作者的建议:

Donot use index merge when single index is good enough

Try to avoid using index merge in UPDATE to not provoke deadlocks

意思是写sql的时候能用一个索引就尽量不要使用两个混合索引去更新,可以先根据索引查询出结果,再执行更新来避免死锁的发生。


参考文档:

https://blog.csdn.net/usst_lidawei/article/details/79494177

你可能感兴趣的:(一次线上死锁问题)