Spring事务失效的原因

问题背景

在业务实现当中,多线程并发操作会带来一些安全问题上的挑战。例如,在秒杀业务中,我们不仅要考虑多线程并发执行时对库存的考虑,还要考虑每个用户的请求是否由一个线程发出,当一个用户的请求由多个线程发出时,可能是脚本代刷的情况,这同样会导致业务出现异常。

方法级别的锁

假设我们的秒杀实现方法是:

	public boolean yourServiceMethod(Long someId) {}

考虑到并发问题,我们选择对方法加锁,

	@Transactional
	public synchronized boolean yourServiceMethod(Long someId) {}

但是这样添加锁,锁的粒度太粗了,在使用锁过程中,控制锁粒度 是一个非常重要的事情,因为如果锁的粒度太大,会导致每个线程进来都会锁住,所以我们需要去控制锁的粒度。这样的加锁方式仍然没法满足防止同一用户的多个线程的请求。

理想的锁粒度

应该是细化到用户级别,而不是整个对象实例。比如在秒杀场景中,我们希望同一个用户的请求被串行化(一个用户不能同时发起多个秒杀请求),但允许不同用户同时进行秒杀。

用户级别的锁

锁的粒度应该根据业务需求进行调整,比如通过 用户ID 进行加锁。可以通过 synchronized(userId) 或者 synchronized(userId.toString().intern()) 来锁定不同用户的请求,保证相同用户在一个时间只能有一个秒杀请求被处理,但允许不同用户并行秒杀。

事务失效

解决了实现用户级别的锁的问题,此时还是存在着问题。即:如果在方法内部加锁,可能会导致当前事务还没有提交,但是锁已经释放也会导致问题。所以选择将当前方法包裹起来,确保事务不会出现问题,同时也保证了锁的粒度:

		// 获取用户ID
        Long userId = user.getId();
        synchronized (userId.toString().intern()) {
            return this.yourServiceMethod(someId);
        }

但是以上的方法仍有问题,因为我们调用的方法其实是this调用的,事务要想生效需要使用代理。
Spring 通过 AOP(面向切面编程) 实现事务管理,具体是通过代理对象来增强目标对象的方法,使其具备事务管理的功能。在代码中,@Transactional 注解被用来标识事务,Spring 会为该方法创建代理,代理对象负责开启、提交或回滚事务。(Spring默认使用JDK动态代理)

        Long userId = user.getId();
        synchronized (userId.toString().intern()) {
            // 获取代理对象(事务)
            Service proxy = (Service) AopContext.currentProxy();
            return proxy.yourServiceMethod(someId);
        }

通过代理进行外部调用,便可以解决事务失效问题。
外部调用 vs. 内部调用:如果是外部调用(从类外部调用目标方法),代理对象会接收到方法调用,并正确处理事务。然而,当类内部的方法调用类内另一个带有 @Transactional 注解的方法(即自调用)时,调用是直接通过 this 对象进行的,绕过了代理对象,导致事务拦截器无法介入处理事务逻辑。

Spring事务失效原因
  • 自调用导致事务失效:Spring的事务管理是通过AOP代理实现的。如果一个类中的方法直接调用另一个标注了@Transactional的方法(自调用),事务不会生效。因为事务代理是在外部调用时才生效,内部调用绕过了代理。
  • 方法不是public
  • 异常处理不当
  • 事务传播行为不当
  • AOP代理类型不匹配:Spring默认使用JDK动态代理处理事务,如果事务类没有实现接口且没有强制使用CGLIB代理,可能会导致事务失效。
  • 数据库不支持事务:某些数据库引擎(如MySQL的MyISAM)不支持事务管理。如果使用了不支持事务的数据库引擎,事务自然不会生效。
  • 多线程环境:Spring的事务管理是基于线程绑定的(ThreadLocal)。如果在多线程环境中使用事务,可能会导致事务失效,因为事务状态在不同线程间无法共享。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值