小言_互联网的博客

seata tcc模式下的一个性能问题

474人阅读  评论(0)
本文解释Seata中,AT模式和MT模式下的一个一阶段的区别。

根据两阶段行为模式的不同,Seata将分支事务划分为2种:

  • Automatic (Branch) Transaction Mode
  • Manual (Branch) Transaction Mode

1.AT模式

AT 模式基于 支持本地 ACID 事务 的 关系型数据库:

一阶段 prepare 行为:在本地事务中,一并提交业务数据更新和相应回滚日志记录。
二阶段 commit 行为:马上成功结束,自动 异步批量清理回滚日志。
二阶段 rollback 行为:通过回滚日志,自动 生成补偿操作,完成数据回滚。

2. MT模式

相应的,MT 模式,不依赖于底层数据资源的事务支持:
一阶段 prepare 行为:调用 自定义 的 prepare 逻辑。
二阶段 commit 行为:调用 自定义 的 commit 逻辑。
二阶段 rollback 行为:调用 自定义 的 rollback 逻辑。
所谓 MT 模式,是指支持把 自定义 的分支事务纳入到全局事务的管理中。

3.一阶段解读

在AT模式下,一阶段会做如下几个操作:

1.解析业务sql;
2.获取sql执行前的镜像,前镜像;
3.执行业务sql;
4.获取sql执行后的镜像,后镜像;
5.添加undo_log日志,把前后镜像数据和业务sql相关的信息组成回滚日志,添加到undo_log中;
6.向TC注册分支事务,并申请相关目标数据的全局锁;
7.事务提交,将业务操作和undo_log一起提交;
8.上报分支事务提交结果给TC;
9.释放本地锁;
10.释放数据库连接;

在AT模式下,一阶段,会有如上的多个步骤,以及解析存储undo_log等操作;那么,在MT模式中,由于prepare逻辑有对应的rollback逻辑,显然这里是不用再添加回滚信息的。那么,这MT模式下一阶段的处理逻辑,是如何避免上述操作带来的性能损耗呢?

4.源码分析

在方法拦截器MethodInterceptor接口下,有一个TCC拦截器实现类TccActionInterceptor,这个实现类有一个invoke方法:

   @Override
    public Object invoke(final MethodInvocation invocation) throws Throwable {
        if(!RootContext.inGlobalTransaction()){
            //not in transaction
            return invocation.proceed();
        }
        Method method = getActionInterfaceMethod(invocation);
        TwoPhaseBusinessAction businessAction = method.getAnnotation(TwoPhaseBusinessAction.class);
        //try method
        if (businessAction != null) {
            //save the xid
            String xid = RootContext.getXID();
            //clear the context
            RootContext.unbind();
            try {
                Object[] methodArgs = invocation.getArguments();
                //Handler the TCC Aspect
                Map<String, Object> ret = actionInterceptorHandler.proceed(method, methodArgs, xid, businessAction,
                        new Callback<Object>() {
                            @Override
                            public Object execute() throws Throwable {
                                return invocation.proceed();
                            }
                        });
                //return the final result
                return ret.get(Constants.TCC_METHOD_RESULT);
            } finally {
                //recovery the context
                RootContext.bind(xid);
            }
        }
        return invocation.proceed();
    }

可以看到,在切面的切入点执行之前,和之后,有2个关键操作:
把xid解绑

//save the xid
String xid = RootContext.getXID();
//clear the context
RootContext.unbind();

恢复xid绑定

//recovery the context
RootContext.bind(xid);

这么做的目的是什么呢?

当把xid解绑后,tcc的这个prepare分支事务执行时,框架不会拦截业务sql进行解析,也不会存储前后镜像和生成undo_log日志,(即使使用了代理数据源,也不会)这样,tcc模式下,就避免了一阶段的上述操作带来的损耗。


转载:https://blog.csdn.net/weixin_39800144/article/details/100515957
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场