TCC事务模型虽然说起来简单,然而要基于TCC实现一个通用的分布式事务框架,却比它看上去要复杂的多,不只是简单的调用一下Confirm/Cancel业务就可以了的。
一、TCC全局事务必须基于RM本地事务来实现
不幸的是,由于[B:Cancel]业务也有n(0<=n<=5)个反向的写库操作,此时一旦[B:Cancel]也中途出错,则后续的[B:Cancel]执行任务更加繁重。
二、TCC事务框架应该接管Spring容器的TransactionManager
首先,根据TCC机制的定义,TCC事务是通过执行Cancel业务来达到回滚效果的。仔细分析一下,这里暗含一个事实:只有生效的Try业务操作才需要执行对应的Cancel业务操作。
,事务是可以在多个(本地/远程)服务之间互相传播其事务上下文的,一个业务方法(Try/Confirm/Cancel)执行完毕并不一定会触发当前事务的commit/rollback操作。
第二、一个业务方法可能会包含多个RM本地事务的情况。
第三、并不是抛出了异常的业务方法,其参与的事务就回滚了。
第四、Spring容器还支持使用setRollbackOnly的方式显式的控制事务完成方向;
后,自行拦截业务方法的拦截器和Spring的事务处理的拦截器还会存在执行先后、拦截范围不同等问题。
三、TCC事务框架应该具备故障恢复机制
四、TCC事务框架应该提供Confirm/Cancel服务的幂等性保障
那么,应该由TCC事务框架来提供幂等性保障?还是应该由业务系统自行来保障幂等性呢?
个人认为,应该是由TCC事务框架来提供幂等性保障。如果仅仅只是极个别服务存在这个问题的话,那么由业务系统来负责也是可以的;
五、TCC事务框架不能盲目的依赖Cancel业务来回滚事务
-
如果TCC事务框架发现某个服务的Try操作的本地事务尚未提交,应该直接将其回滚,而后就不必再执行该服务的cancel业务; -
如果TCC事务框架发现某个服务的Try操作的本地事务已经回滚,则不必再执行该服务的cancel业务; 如果TCC事务框架发现某个服务的Try操作尚未被执行过,那么,也不必再执行该服务的cancel业务。
-
已生效的Try操作应该被其Cancel操作所回撤; 尚未生效的Try操作,则不应该执行其Cancel操作。这一点,不是幂等性所能解决的问题。如上文所述,幂等性是指服务被执行一次和被执行n(n>0)次所产生的影响相同。但是,未被执行和被执行过,二者效果肯定是不一样的,这不属于幂等性的范畴。
六、Cancel业务与Try业务并行,甚至先于Try操作完成
-
将[B:Try]的本地事务标注为rollbackOnly,阻止其后续生效; -
禁止其再次将事务上下文传递给其他远程分支,否则该问题将在其他分支上出现; -
相应地,[B:Cancel]也不必执行,至少不能生效。
当然,TCC事务框架也可以简单的选择阻塞[B:Cancel]的处理,待[B:Try]执行完毕后,再根据它的执行情况判断是否需要执行[B:Cancel]。 不过,这种处理方式因为需要等待,所以,处理效率上会有所不及。
-
Confirm业务在Try业务之后执行,若发现并行,则只能阻塞相应的Confirm业务操作; 在进入Confirm执行阶段之后,也不可以再提交同一全局事务内的新的Try操作的RM本地事务。
七、TCC服务复用性是不是相对较差?
八、TCC服务是否需要对外暴露三个服务接口?
九、TCC服务A的Confirm/Cancel业务中能否调用它依赖的TCC服务B的Confirm/Cancel业务?
这里,给大家推荐一款开源的TCC分布式事务管理器ByteTCC