一、声名式事务和编程式事务
声名式事务:
即通过注解开启的事务 @Transactional
编程式事务:
Spring 提供了一个特殊类 TransactionTemplate 用来灵活在代码中实现spring 事务
@Autowired
private TransactionTemplate transactionTemplate;
...
public void save(Xxxx xxx) {
// .......其他代码
transactionTemplate.execute((status) => {
// 需要加入事务的代码.....
return Boolean.TRUE;
})
}
二、大事务
即 通过@Transactional 开启方法上的事务或者通过方法名来控制哪些方法开启事务时,方法内部若有大量的数据库查询,或者外部调用,或者数据量较大时会让整个事务占用的时间和资源都变大。
问题
这样就可能造成:
死锁,接口超时,回滚时间长,锁等待,数据库主从延迟,并发时数据库链接被占满等问题。
解决
解决方法大致有以下:
一、 减少 @Transactional 注解的使用,改用上面的编程式事务
如果方法内实际比较简单可以直接使用 @Transactional 也是可以的,但内部较为复杂时,通过编程式事务去控制极少数需要事务的方法即可,其他的无需在事务中。
二、 若存在大量 SELECT 查询则可以将查询放到事务之外
如果在同一方法内要避免内部调用 this 的方式,否则注解事务会失效,可以通过 再次注入自己或者 ((ServiceA)AopContext.currentProxy()).方法名(xxxx);
三、 事务中尽量避免外部调用(调用其他系统接口,redis,mq 等)
可能会由于网络不稳定而引起错误或者时间较长,较长时就可能变成大事务。为了保持外部调用的数据一致性,可以考虑重试或者其他补偿机制。
四、 避免大批量数据操作
在一次事务中避免一次性处理大量的数据,一次几百或者上千的更新操作。改为小批量,一次几十,多次执行,避免一次事务时间过长成为大事务或引起性能问题。
五、 是否一定需要事务
部分业务或者非主业务,在允许个别细微差错的情况下可以不添加事务,减少需要加入到事务中的内容
六、 是否可以异步处理
如果业务上没有强关联性,可以通过异步的方式在其他地方另起线程事务执行那么就使用异步的方式,减少在同一同步事务中加入过多的内容而出现大事务的情况。