spring中,大事务问题
上一次,和朋友们聊了聊事务失效场景,我已经整理成了文章,Java开发中,导致事务失效的场景。紧接着有提到了大事务问题,经过一番激烈的讨论,对这个问题也算是有了一些输出场景。下面我简要描述一下。
一、概念
什么是大事务?简要来说就是:运行时间比较长,操作的数据比较多的事务。
二、大事务引发的问题
大事务对系统来说,是导致响应缓慢的一个重要原因。它会引发一些列问题,诸如:锁等待、死锁、接口超时、数据库主从延迟、事务回滚时间长、数据库连接池被占满等问题。
三、形成原因或解决办法
1. 将查询放到事务外处理。
@Transactional(rollbackFor=Exception.class)
public void save(Entity entity) {
selectData();
addData(entity );
updateData(entity );
}
如上代码所示,一个事务中,同事包含了查询,新增,修改,这种时候,就容易出现大事务。当然,改造也很简单,把查询的放到事务外。这里就要引入另一种事务实现方式:编程式事务。上述代码是:声明式事务。
@Autowired
privateTransactionTemplate transactionTemplate;
public void save(Entity entity) {
selectData();
transactionTemplate.execute((obj) => {
addData(entity );
updateData(entity );
returnBoolean.TRUE;
})
}
2.尽量少用声明式事务@Transactional
我相信这种事务使用场景应该是非常广泛的,包括我所参与的项目中,使用这样的方式来配置事务占有率也是非常高的,但是为什么会建议少使用这种方式呢?
2.1 我们都知道,spring事务其实就是通过aop切面来起作用的,如果使用不当,会使得事务失效,详见我的上一篇文章。
2.2 这种事务声明,一般是用于业务代码中的某个方法,这种方法内,往往可能存在多种数据库操作,这种时候,就容易产生大事务,如上述例子。解决方法依然是:使用编程式事务来管控。
3.事务中,存在RPC调用
在事务中,如果使用了外部的远程调用、内部业务调用、MQ或Redis等,因为调用过程中的不可控的网络延迟因素,导致大事务发生或产生异常导致事务回滚。
@Transactional(rollbackFor=Exception.class)
public void save(Entity entity) {
feignClient.read();
redisTemplate.get();
addData();
}
解决办法依然是,把需要添加事务的部分,添加事务,最常用的依然是编程式事务。
@Autowired
privateTransactionTemplate transactionTemplate;
public void save(Entity entity) {
feignClient.read();
redisTemplate.get();
transactionTemplate.execute((obj) => {
addData();
updateData();
returnBoolean.TRUE;
})
}
4.异步处理
如果在业务逻辑中,需要做多步骤、多阶段操作或消息发送等,我们可以采取异步操作来处理这部分事情。把事务尽量做到精确化。
@Autowired
privateTransactionTemplate transactionTemplate;
public void save(Entity entity) {
transactionTemplate.execute((obj) => {
addData(entity);
returnBoolean.TRUE;
})
sendMessage();
}