1.使用@Transactional注解嵌套事务的情况
关键词: 全表锁 父子事务 嵌套事务 两个update语句 事务挂起 全表扫描 next-key 锁 记录锁 间隙锁
代码介绍
调用
直接死锁报错(无慈悲)
案例结语
这个问题同时需要考虑的有两点。第一点是事务和锁机制,第二点是@Transactional注解的事务提交规则。
update在执行的时候,where中最终没有使用到索引的话,会导致给所有记录加上锁(next-key,记录锁+间隙锁)这样就出现了全表锁。若此时进行新事务的update操作,那么新的事务操作就需要等待对应记录的锁被释放掉才行。但重点在于testSonTransactional1方法上,它使用了Propagation.REQUIRES_NEW的这一事务传播行为。而这一传播行为的意思是如果当前存在事务,那么挂起当前的事务并开启一个全新的事务。这就相当于独立出去一个新的事务。当这新事务的新update语句执行时,由于上一个事务中的update进行了全表锁,所以它需要等上一个事务执行结束并释放锁。但这个时候问题就出现了,由于@Transactional注解的testSonTransactional方法需要等待当前方法内的任务执行结束之后才能进行事务提交,因而它也需要等待testSonTransactional1方法执行结束后才能提交事务,而testSonTransactional1方法也需要等待testSonTransactional方法提交事务并释放锁才能执行完任务并提交事务,因此这时死锁就出现了。
变种案例1 方法封装的情况
描述
依旧死锁
结语
和一开始那个案例一样,只不过这次的update语句是分作两个方法执行。这里的testSonTransactional2虽然也开启了事务,但是使用的是默认的事务,默认的事务是不会开启新的事务的,就相当于它在上一层方法的事务之中。
它默认的事务传播类型是Propagation.REQUIRED
变种案例2 where使用索引的情况
描述
图2.1
使用egg的id进行update操作依旧死锁
图2.2
换个姿势:
图2.3
还是死锁(报错的图我就不放了)
那再换一种姿势两条sql语句都使用id进行更新:
图2.4
但仍然死锁(报错图不放)
其实只要更新的目标错开就可以了
图2.5
图2.6
结语
其实这个案例死锁的原因还是和原本的案例是一样的。都是在操作相同的记录导致相互等待的死锁。图2.1、图2.3、图2.4中的sql即使用了索引列,但是依旧无法在这种方法调用的情况下避免死锁。
不过一般情况下两个独立的事务且在代码上没有这种方法的调用情况,即使两个事务update操作的目标有交集,也是不会出现这种类型的死锁的。
版权归原作者 jcj_gyjf 所有, 如有侵权,请联系我们删除。