你知道Seata AT模式中前后镜像是如何生成的嘛?

简介: 你知道Seata AT模式中前后镜像是如何生成的嘛?

前言

Seata官网中,我们可以知道AT模式一阶段的处理流程如下:

1.解析 SQL:得到 SQL 的类型(UPDATE),表(product),条件(where name = 'TXC')等相关的信息。

2.查询前镜像:根据解析得到的条件信息,生成查询语句,定位数据。

3.执行业务 SQL。

4.查询后镜像:根据前镜像的结果,通过 主键 定位数据。

5.插入回滚日志:把前后镜像数据以及业务 SQL 相关的信息组成一条回滚日志记录,插入到 UNDO_LOG 表中

......

前镜像的作用是保证在分布式事务失败时能够成功回滚的重要依据,后镜像是在回滚前校验是否脏写的数据依据,那么我们一阶段的前后镜像在真实的代码实现中是如何生成的呢?

前后镜像的生成

为了能够探寻到前后镜像的生成原理,我们需要看一看seata源码。最终我们把入口定位到AbstractDMLBaseExecutor.executeAutoCommitFalse()方法:

protected T executeAutoCommitFalse(Object[] args) throws Exception {
        // 获取前镜像
        TableRecords beforeImage = beforeImage();
        // 执行业务SQL
        T result = statementCallback.execute(statementProxy.getTargetStatement(), args);
        // 获取后镜像
        TableRecords afterImage = afterImage(beforeImage);
        // 存储前后镜像
        prepareUndoLog(beforeImage, afterImage);
        return result;
    }
复制代码
  • 前镜像

继续深入探究一下到底是怎么获取beforeImage的,根据源码来看,我们发现beforeImage()方法有很多实现:

image.png

也就是说,Seata会根据不同的业务SQL来生成beforeImage,有点经验的小伙伴能够看出,这里其实使用到了模版模式加上策略模式,我们挑一个DeleteExecutor来看一下:

@Override
    protected TableRecords beforeImage() throws SQLException {
        SQLDeleteRecognizer visitor = (SQLDeleteRecognizer) sqlRecognizer;
        // 根据表名解析出元数据
        TableMeta tmeta = getTableMeta(visitor.getTableName());
        ArrayList<List<Object>> paramAppenderList = new ArrayList<>();
        // 生成查询beforeImage的SQL语句
        // SELECT [列名,] FROM [表名] (别名) (WHERE) (ORDER BY) (LIMIT) FOR UPDATE
        String selectSQL = buildBeforeImageSQL(visitor, tmeta, paramAppenderList);
        // 执行SQL查询beforeImage
        return buildTableRecords(tmeta, selectSQL, paramAppenderList);
    }
复制代码

1.关键原理就是根据业务SQL反向查询出被影响的数据;

2.为了保证查询到的数据不是快照数据,一定要记得加上FOR UPDATE

另外的话,我们发现其实UpdateExecutor的前镜像生成方式和DeleteExecutor也差不多,像普通的insert这种SQL的前镜像就更简单了:

@Override
    protected TableRecords beforeImage() throws SQLException {
        return TableRecords.empty(getTableMeta());
    }
复制代码

因为普通insert语句不存在任何前镜像,所以直接返回空记录;

  • 后镜像

我们再来看一下后镜像是如何生成的,这次我们看一下UpdateExecutor的后镜像生成方法afterImage():

@Override
    protected TableRecords afterImage(TableRecords beforeImage) throws SQLException {
        // 获取元数据
        TableMeta tmeta = getTableMeta();
        // 没有前镜像,也不存在后镜像,说明没有数据被修改
        if (beforeImage == null || beforeImage.size() == 0) {
            return TableRecords.empty(getTableMeta());
        }
        // 生成查询后镜像SQL
        // SELECT [列名,] FROM [表名] (别名) WHERE 主键 in (前镜像的主键值)
        // 这里面有一个配置项[client.undo.onlyCareUpdateColumns],是否只关心被修改的列名,默认是true
        String selectSQL = buildAfterImageSQL(tmeta, beforeImage);
        ResultSet rs = null;
        try (PreparedStatement pst = statementProxy.getConnection().prepareStatement(selectSQL)) {
            SqlGenerateUtils.setParamForPk(beforeImage.pkRows(), getTableMeta().getPrimaryKeyOnlyName(), pst);
            // 执行查询后镜像
            rs = pst.executeQuery();
            // 包装查询结果
            return TableRecords.buildRecords(tmeta, rs);
        } finally {
            IOUtil.close(rs);
        }
    }
复制代码

后镜像的生成原理与前镜像的生成原理差不多,不过还是有一些小小的区别的:

1.后镜像的查询条件使用的是前镜像对应的主键值,就没有用业务SQL的查询条件;不同的Executor处理方式不同,需要根据具体的业务SQL来区分;

2.查询后镜像的SQL没有使用FOR UPDATE加锁,直接拿的快照数据;

小结

通过对seata源码的分析,我们现在已经了解了前后镜像的生成原理了:

1.通过业务SQL来判断SQL语句的类型,从而选择不同的Executor来获取前后镜像;

2.前镜像是通过业务SQL的查询条件,并加上FOR UPDATE来查询业务SQL执行前的数据;(不同的Executor实现不同)

3.后镜像是在业务SQL执行完毕后,根据前镜像内的主键数据来获取的数据;(不同的Executor实现不同)

4.通过前后镜像的多种实现可以判断出seata AT模式所支持的SQL语句的所有类型;


作者:梦想实现家_Z

链接:https://juejin.cn/post/7164311701595095053

来源:稀土掘金

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

相关文章
|
6月前
Seata框架在AT模式下是如何保证数据一致性的?
通过以上这些机制的协同作用,Seata 在 AT 模式下能够有效地保证数据的一致性,确保分布式事务的可靠执行。你还可以进一步深入研究 Seata 的具体实现细节,以更好地理解其数据一致性保障的原理。
212 50
SEATA模式
Seata 是一款开源的分布式事务解决方案,支持多种事务模式以适应不同的应用场景。其主要模式包括:AT(TCC)模式,事务分三阶段执行;TCC 模式,提供更灵活的事务控制;SAGA 模式,基于状态机实现跨服务的事务一致性;XA 模式,采用传统两阶段提交协议确保数据一致性。
96 5
Apache Seata 如何解决 TCC 模式的幂等、悬挂和空回滚问题
【6月更文挑战第8天】Apache Seata 是一款分布式事务框架,解决TCC模式下的幂等、悬挂和空回滚问题。通过记录事务状态处理幂等,设置超时机制避免悬挂,明确标记Try操作成功来处理空回滚。Seata 提供丰富配置和管理功能,确保分布式事务的可靠性和效率,支持复杂事务处理场景,为企业业务发展提供支持。
392 7
Seata常见问题之xa模式出现错误xid is not valid如何解决
Seata 是一个开源的分布式事务解决方案,旨在提供高效且简单的事务协调机制,以解决微服务架构下跨服务调用(分布式场景)的一致性问题。以下是Seata常见问题的一个合集
369 4
Seata常见问题之xa模式下插入一条数据再更新这条数据会报错如何解决
Seata 是一个开源的分布式事务解决方案,旨在提供高效且简单的事务协调机制,以解决微服务架构下跨服务调用(分布式场景)的一致性问题。以下是Seata常见问题的一个合集
258 2
Seata常见问题之Seata AT的设计不支持使用临时表如何解决
Seata 是一个开源的分布式事务解决方案,旨在提供高效且简单的事务协调机制,以解决微服务架构下跨服务调用(分布式场景)的一致性问题。以下是Seata常见问题的一个合集
Seata常见问题之项目一直启动不成功如何解决
Seata 是一个开源的分布式事务解决方案,旨在提供高效且简单的事务协调机制,以解决微服务架构下跨服务调用(分布式场景)的一致性问题。以下是Seata常见问题的一个合集
906 0
在Java中使用Seata框架实现分布式事务的详细步骤
通过以上步骤,利用 Seata 框架可以实现较为简单的分布式事务处理。在实际应用中,还需要根据具体业务需求进行更详细的配置和处理。同时,要注意处理各种异常情况,以确保分布式事务的正确执行。
|
2月前
|
SQL
seata是怎么进行分布式事务控制的
seata是怎么进行分布式事务控制的
|
6月前
|
如何在Seata框架中配置分布式事务的隔离级别?
总的来说,配置分布式事务的隔离级别是实现分布式事务管理的重要环节之一,需要认真对待和仔细调整,以满足业务的需求和性能要求。你还可以进一步深入研究和实践 Seata 框架的配置和使用,以更好地应对各种分布式事务场景的挑战。
212 63
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等