这两天遇到了一个死锁的问题,下面是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

我们知道mysql 事务具有acid属性,分别是代表原子性,隔离性,一致性,持久性。
根据原子性可以知道事务操作是不可再分的,每个事务要么全部成功要么全部失败,通过隔离性可以知道:事务之间不会相互影响。
但是本次为什么还出现了并发事务出现了死锁问题呢?
根据死锁日志可以提取几个有用信息:

首先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
网友评论