SpringBoot使用@Transactional注解事务死锁的情况分析

文章探讨了在Java中使用@Transactional注解处理嵌套事务时可能出现的死锁问题。主要关注点包括全表锁、父子事务、更新语句的索引使用以及事务的提交规则。当update操作未使用索引导致全表扫描时,会引发全表锁,进而可能导致死锁。变种案例分析了不同方法封装和更新策略下的死锁情况,即使使用了索引,只要更新目标相同,仍可能产生死锁。关键在于事务间的依赖和锁的持有顺序导致的循环等待。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

 

1.使用@Transactional注解嵌套事务的情况

代码介绍

案例结语

变种案例1 方法封装的情况

描述

​结语

变种案例2 where使用索引的情况

描述

结语


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操作的目标有交集,也是不会出现这种类型的死锁的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值