文章目录
一.基础
1.1 本地写 + rpc写
本地写事务和rpc写操作(最好)不要放在一个事物中。(除非有重试机制)
因为若本地写事务成功了,rpc写操作ReadTimeout执行失败(但是实际上,改rpc操作已经在远程成功了),会导致,本地写操作回滚。造成,本地未写,远程写成功了。
- 场景1:
1.本地写
2.rpc写
若rpc写失败了,本地成功了。就会导致,不一致。
虽然rpc失败,RuntimeException也抛出来了,并且在最外层也catch住了。这样前端会有错误信息提示。如果用户手动再点一次,就能达到重试的效果,这样,如果本地写幂等了,那么rpc写还有一次重新执行的机会。但是如果用户一看报错了,很蒙圈,直接溜了,不操作了。那么此时就会出现本地数据和远端数据不一致的情况。
- 场景2:
1、本地写
2、rpc写
如果rpc失败了,用户一看报错就溜了。此时数据不一致了。但是用户下次操作,仍然会从这个报错的地方开始。无形中形成了重试机制,所以能有再次执行rpc的机会。这种场景,本地写和rpc写可以放在一次,允许短暂的数据不一致情况(或者数据不一致和这个人绑定,对其他人的其他数据,没有影响)
- 场景3:
两个写操作(相同的db,不同的表。但都是本地写),直接事务包一起即可
1.2 transaction事务注解
-
本地的两个表的写操作一定要加事务注解
-
rpc 写和 本地的写操作,可以使用最大努力重试异步,保证成功。或者是 job触发的,可以在保证幂等的情况下,执行3次job。同样能达到重试的机制
-
本地的写操作 &&一些列rpc操作:事务最大努力重试保证成功
@Transaction{
本地写
@最大努力重试
rpc
}
二、@Transactional使用注意事项
2.1 踩坑-Methods should not call same-class methods with incompatible “@Transactional” values问题
方法和调用事务的方法,在同一个类中
-
问题描述
-
解决
((RdcRefundOutboundBillStatusChangeJob) AopContext.currentProxy()).processBill(bill, param.getDestroyDayLimit()) @Transactional(rollbackFor = Exception.class) public boolean processBill(RefundOutboundBillPO bill, int destroyDayLimit) { }
@Component public class AsyncService implements ApplicationContextAware { private ApplicationContext applicationContext; public void async1() {//类的某个方法a // 使用AppicationContext来获得动态代理的bean,然后再执行你调用的方法 this.applicationContext.getBean(AsyncService.class).async2(); } @Transactional public void async2() { //本地事务1 //本地事务2 } // 注入ApplicationContext @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } }
2.1、事务使用注意事项
https://www.cnblogs.com/keeya/p/11180612.html
2.2、事务失效场景
https://segmentfault.com/a/1190000040129676
https://zhuanlan.zhihu.com/p/339536376
类上时,该类所有的public方法都会开启事务;放在方法上时,表示当前方法支持事务,必须在public方法上才可以
需要加在实现类上,而不是方法上
在@Transactional注解中有一个属性rollbackFor,这个属性设置的是发生什么类型的异常时事务进行回滚,默认的是RuntimeException。
2.3、踩坑-多数据源,某一数据源未配置事务name,导致事务失效
-
问题描述
NoUniqueBeanDefinitionException: No qualifying bean of type ‘org.springframework.transaction.PlatformTransactionManager’ available: expected single matching bean but found 2: default
-
原因
原因
多数据源的时候
1、第一个数据源,事务默认名称为default,第二个数据源事务名称为sale
2、当第一个数据源,使用事务,但是,没有指定事务处理器,就会报这个错误。
3、因为每个数据源都有自己的事务配置,单纯地用@Transactional 没法确定是哪个事务处理
解决:
指定要使用的具体的事务管理器
public @interface Transactional {
@AliasFor("transactionManager")
String value() default "";
@AliasFor("value")
String transactionManager() default "";
在@Transactional 注解上,通过value属性或transactionManager = “指定具体的事务管理器名称”