@Transactional导致的长事务

导致长事务原因

长事务是因为在事务中作了大量耗时的操作,如调用第三方,保存过多的数据,执行了与事务不相关的操作。

@Service
public class UserService {
    @Transactional
    public void updateUser(){
        // 调用第三方(第三方耗时)
        // 执行更新sql
        this.updateUserById()
    }
}

上述方法就是在@Transactional的方法中,执行业务逻辑时,有耗时操作,@Transactional在调用该方法时,从数据库连接池中获取到了连接,且当前连接一直占用
长事务产生原因

  1. 大量锁竞争
  2. 事务中耗时操作
  3. 调用第三方(请求设置超时时间60s)

导致的问题

  • 数据库连接池被占满,应用无法获取连接资源;
  • 锁定太多的数据,造成大量的阻塞和锁超时;

数据库连接池被占满,应用无法获取连接资源,根据上一篇分析@Transational原理知道调用了updateUser方法时,代理会获取到connection连接,并开启事务,没有提交事务时,会一直占用该连接,大量调用时,就容易导致数据库连接池占满

锁太多数据,是因为更新操作操作很多数据时,这些数据获得了行锁,这些行锁在提交事务后释放掉,如果没有被释放,其他需要获取写入锁时,都得等待。从而导致阻塞和锁超时

如何避免长事务

解决长事务的宗旨就是对事务方法进行拆分,尽量让事务变小,变快,减小事务的颗粒度

  • 在一个事务里面,避免一次处理太多数据;
  • 在一个事务里面,尽量避免不必要的查询;
  • 在一个事务里面,避免耗时太多的操作,造成事务超时。一些非DB的操作,比如rpc调用,消息队列的操作尽量放到事务之外操作;尽量避免在事务中调用第三方
拆分

声明式事务中(@Transactional),将一些查询放在事务外面,A类的a方法查询判断逻辑,然后a调用B类的事务b方法进行更新操作。

@Service
public class A {
    @Autowired
    private B b;

    public voie test(){
        // 查询
        this.findUser();
        b.update()
    }
}

@Service
public class B {
    @Transactional
    public void update(){
       // 执行sql 
    }
}
编程式事务

基于底层的API,开发者在代码中手动的管理事务的开启、提交、回滚等操作。在spring项目中可以使用TransactionTemplate类的对象,手动控制事务。

@Autowired 
private TransactionTemplate transactionTemplate; 

//... 

public void save(RequestBill requestBill) { 
    transactionTemplate.execute(transactionStatus -> {
        requestBillDao.save(requestBill);
        //保存明细表
        requestDetailDao.save(requestBill.getDetail());
        return Boolean.TRUE; 
    });
}

此种方式也是阿里巴巴java开发手册中推荐的使用方法,此种需要多写一些代码。但是此种方式也是无法避免长事务,也是需要开发者注意不能再事务中处理耗时任务。

事务后处理
@Transactional(rollbackFor = Exception.class)
public Boolean inviteUser(..) {
    userService.save(..);
    // 事务提交后再处理
    TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
        @Override
        public void afterCommit() {
            // 调用第三方
        }
});

TransactionSynchronizationManager.registerSynchronization注册一个处理器,在事物提交后处理业务。
image.png

事务提交后处理,并不能保证第三方一定能成功,这就可能导致事务不一致。如果不是强一致的事物时,通过人工兜底的方式来处理事务不一致。人工补偿的方式在程序设计中,也是很常见的,利用中间件rocketmq来解决事务不一致时,其实也需要人工补偿的方案的。

总结

在编码过程中,需要考虑到在事务中避免做耗时的操作。如果在事务中调用第三方,则需要设置请求相对较短的超时时间。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值