apache / incubator-seata

:fire: Seata is an easy-to-use, high-performance, open source distributed transaction solution.
https://seata.apache.org/
Apache License 2.0
25.35k stars 8.79k forks source link

请教关于AT模式事务边界的问题 #6894

Closed wxrqforever closed 1 month ago

wxrqforever commented 1 month ago

假设AT模式下,有A、B两个应用,其中A应用作为上游有接口a方法,调用B应用的b方法,有如下代码摘要场景。 应用A:

@GlobalTransactional
void a(){
    //http调用B的b方法
    call B.b();
    //A自己的一些逻辑
    logic();
}

应用B:

void b(){
  //写调用日志,不希望自动回滚
  writeHistory();
  //B业务逻辑部分
  logicB();
}

void writeHistory(){
    //写日志表
}

@Transactional
void logicB(){
  //写业务信息到db里
  writeBiz();
}

我的疑问是: 从我看的代码实现和简单的用例测试来看,似乎b方法执行期间的所有写操作都会被AT拦截生成,生成前后镜像,即使writeHistory没有使用@Transactional,即autoCommit为false的,也会这么处理。

public T doExecute(Object... args) throws Throwable {
        AbstractConnectionProxy connectionProxy = statementProxy.getConnectionProxy();
        if (connectionProxy.getAutoCommit()) {
            //autoCommit为true也会被改成false然后走executeAutoCommitFalse逻辑
            return executeAutoCommitTrue(args);
        } else {
            return executeAutoCommitFalse(args);
        }
    }
protected T executeAutoCommitFalse(Object[] args) throws Exception {
        try {
            TableRecords beforeImage = beforeImage();
            T result = statementCallback.execute(statementProxy.getTargetStatement(), args);
            TableRecords afterImage = afterImage(beforeImage);
            prepareUndoLog(beforeImage, afterImage);
            return result;
        } catch (TableMetaException e) {
            LOGGER.error("table meta will be refreshed later, due to TableMetaException, table:{}, column:{}",
                e.getTableName(), e.getColumnName());
            statementProxy.getConnectionProxy().getDataSourceProxy().tableMetaRefreshEvent();
            throw e;
        }
    }

因为会回滚所有的写操作,在接入的时候,就需要对writeHistory进行修改增加解绑逻辑,而且对于下游而言增加这段代码是因为上游以全局事务的方式进行调用。

void writeHistory(){
    //增加解绑逻辑
    String unbind = RootContext.unbind();
    //写日志表
    RootContext.bind(unbind);
}

不知我的理解是否正确。如果是的话,我想请教一下为啥不仅把@Transactional范围内的事务进行回滚,这不是更符合全局事务回滚=各事务参与者本地事务回滚的语义吗?

以上,期望您的回复,感谢您的答疑。

funky-eyes commented 1 month ago

@transactional 属于spring,Seata并不强依赖spring,而且@transactional和@Globaltransactional注解是可以配合使用的,开启本地事务下可以将内部多个dml动作合并为一次分支注册,对性能有很大提升,为什么要忽略非@transactional的场景?难道非@transactional 注解的方法就不需要分布式事务保证了?判断依据是什么呢? 如果你某个动作下不需要加入分布式事务,请查看@Globaltransactional 上的事务传播能力。 @Transactional belongs to Spring, and Seata does not strongly depend on Spring. The @Transactional and @GlobalTransactional annotations can be used together. By enabling local transactions, multiple DML actions within can be combined into a single branch registration, significantly improving performance. Why ignore scenarios without @Transactional? Does that mean methods without the @Transactional annotation do not require distributed transaction guarantees? What is the basis for this judgment?

If you don't need a distributed transaction for a specific action, please refer to the transaction propagation capabilities of @GlobalTransactional.

wxrqforever commented 1 month ago

oh,确实没有考虑到比如只有一个写操作的情况,这种不会使用@Transactional