Seata AT 模式代码级详解

seata源码分析之如何控制下游服务的提交与回滚 概览 seata client跟维持了一个连接,监听server端的消息,根据接到的消息判断是提交还是回滚。 客户端建立流程: GlobalTransactionScanner.afterPropertiesSet() GlobalTransactionScanner.initClient() TMClient.init TmRpcClient.getInstance new TmRpcClient new ... 阅读详情

cafcc4951a505c170e9710615b90b54f.gif

文|

刘月财

seata-go 项目负责人

北京小桔科技有限公司【滴滴】开发工程师

赵新(花名:于雨 )

蚂蚁集团 Seata 项目开源负责人

本文 5343 字 阅读 14 分钟

背景

Seata 四种事务模式中,AT 事务模式是阿里体系独创的事务模式,对业务无侵入,也是 Seata 用户最多的一种事务模式,兼具易用性与高性能。

目前,Seata 社区正大力推进其多语言版本建设,Go、PHP、JS 和 Python 四个语言版本基本完成了 TCC 事务模式的实现。参照 Seata v1.5.2 版本的 AT 模式的实现,并结合 Seata 官方文档,本文尝试从代码角度详解 Seata AT 事务模式的详细流程,目的是梳理 Seata Java 版本 AT 模式的实现细节后,在多语言版本后续开发中,优先实现 AT 事务模式。

1、什么是 AT 模式?

AT 模式是一种二阶段提交的分布式事务模式,它采用了本地 undo log 的方式来数据在修改前后的状态,并用它来实现回滚。从性能上来说,AT 模式由于有 undo log 的存在,一阶段执行完可以立即释放锁和连接资源,吞吐量比 XA 模式高。用户在使用 AT 模式的时候,只需要配置好对应的数据源即可,事务提交、回滚的流程都由 Seata 自动完成,对用户业务几乎没有入侵,使用便利。

2、AT 模式与 ACID 和 CAP

谈论数据库的事务模式,一般都会先谈论事务相关的 ACID 特性,但在分布式场景下,还需要考虑其 CAP 性质。

2.1 AT 与 ACID

在数据库本地事务隔离级别读已提交(Read Committed)或以上的基础上,Seata(AT 模式)的默认全局隔离级别是读未提交(Read Uncommitted)。

如果应用在特定场景下,必须要求全局的读已提交,目前 Seata 的方式是通过 SELECT FOR UPDATE 语句的代理。 

SELECT FOR UPDATE 语句的执行会查询全局锁,如果全局锁被其他事务持有,则释放本地锁(回滚 SELECT FOR UPDATE 语句的本地执行)并重试。这个过程中,查询是被 block 住的,直到全局锁拿到,即读取的相关数据是已提交的,才返回。

出于总体性能上的考虑,Seata 目前的方案并没有对所有 SELECT 语句都进行代理,仅针对 FOR UPDATE 的 SELECT 语句。

详细例子参考 Seata 官网:https://seata.io/zh-cn/docs/dev/mode/at-mode.html

2.1.2 AT 模式的写隔离

AT 会对写操作的 SQL 进行拦截,提交本地事务前,会向 TC 获取全局锁,未获取到全局锁的情况下,不能进行写,以此来保证不会发生写冲突:

• 一阶段本地事务提交前,需要确保先拿到全局锁;

• 拿不到全局锁,不能提交本地事务;

• 拿全局锁的尝试被限制在一定范围内,超出范围将放弃,并回滚本地事务,释放本地锁。

详细例子参考 Seata 官网:https://seata.io/zh-cn/docs/dev/mode/at-mode.html

2.2 AT 与 CAP

Seata 所有的事务模式在一般情况下,是需要保证 CP,即一致性和分区容错性,因为分布式事务的核心就是要保证数据的一致性(包括弱一致性)。比如,在一些交易场景下,涉及到多个系统的金额的变化,保证一致性可以避免系统产生资损。

分布式系统不可避免地会出现服务不可用的情况,如 Seata 的 TC 出现不可用时,用户可能希望通过服务降级,优先保证整个服务的可用性,此时 Seata 需要从 CP 系统转换为一个保证 AP 的系统。

比如,有一个服务是给用户端提供用户修改信息的功能,假如此时 TC 服务出现问题,为了不影响用户的使用体验,我们希望服务仍然可用,只不过所有的 SQL 的执行降级为不走全局事务,而是当做本地事务执行。

AT 模式默认优先保证 CP,但提供了配置通道让用户在 CP 和 AP 两种模式下进行切换:

• 配置文件的 tm.degrade-check 参数,其值为 true 则分支事务保证 AP,反之保证 CP;

•手动修改配置中心的 service.disableGlobalTransaction 属性为 true,则关闭全局事务实现 AP。

3、AT 数据源代理

在 AT 模式中,用户只需要配置好 AT 的代理数据源即可, AT 的所有流程都在代理数据源中完成,对用户无感知。 

AT 数据源代理的整体类结构如下图:

5b39c5929852aa517d24f7c5bc4df942.png

 AT 事务数据源代理类结构图【from https://seata.io/zh-cn/docs/dev/mode/xa-mode.html】

AT 的数据源代理中,分别对目标数据库的 DataSource 、 Connection 和 Statement  进行了代理,在执行目标 SQL 动作之前,完成了 RM 资源注册、 undo log 生成、分支事务注册、分支事务提交/回滚等操作,而这些操作对用户并无感知。

下面的时序图中,展示了 AT 模式在执行过程中,这几个代理类的动作细节:

e9500c6df3c1db03b17dc1da6378640e.png

注:图片建议在 PC 端查看

4、AT 模式流程

以下是 AT 模式的整体流程,从这里可以看到分布式事务各个关键动作的执行时机,每个动作细节,我们后面来讨论:

e495467ed0e21cdd468175722a5f2969.png

注:图片建议在 PC 端查看

4.1 一阶段

在 AT 模式的第一阶段, Seata 会通过代理数据源,拦截用户执行的业务 SQL ,假如用户没有开启事务,会自动开启一个新事务。如果业务 SQL 是写操作(增、删、改操作)类型,会解析业务 SQL 的语法,生成 SELECT SQL 语句,把要被修改的记录查出来,保存为 “before image” 。然后执行业务 SQL ,执行完后用同样的原理,将已经被修改的记录查出来,保存为 “after image” ,至此一个 undo log 记录就完整了。随后 RM 会向 TC 注册分支事务, TC 侧会新加锁记录,锁可以保证 AT 模式的读、写隔离。RM  再将 undo log 和业务 SQL 的本地事务提交,保证业务 SQL 和保存 undo log 记录 SQL 的原子性。

dddcb6f7038c133bfb2c4b1d6ae51f53.png

4.2 二阶段提交

AT 模式的二阶段提交,TC 侧会将该事务的锁删除,然后通知 RM 异步删除 undo log 记录即可。

059c331b56ca2816fea164d90a11323e.png

4.3 二阶段回滚

如果 AT 模式的二阶段是回滚,那么 RM 侧需要根据一阶段保存的 undo log 数据中的 before image 记录,通过逆向 SQL 的方式,对在一阶段修改过的业务数据进行还原即可。

但是在还原数据之前,需要进行脏数据校验。因为在一阶段提交后,到现在进行回滚的中间这段时间,该记录有可能被别的业务改动过。校验的方式,就是用 undo log 的 after image 和现在数据库的数据做比较,假如数据一致,说明没有脏数据;不一致则说明有脏数据,出现脏数据就需要人工进行处理了。

71766b8bcb77dbc4ccd79af808ffe083.png

5、关键代码模块

如下是 AT 模式整个流程的主要模块,我们从中可以了解开发 AT 模式需要做哪些事情:

32ec3ade569fc01198dd160bf87baf6f.png

5.1 Undo log数据格式

undo log 存在表 undo_log 表中,undo_log 表的表结构如下:

d615ece43c2ceb35fe81ac757208831d.png

rollback_info 存放了业务数据修改前后的内容,数据表存放的是经过压缩后的格式,他的明文格式如下:

{
    "branchId":2828558179596595558,
    "sqlUndoLogs":[
        {
            "afterImage":{
                "rows":[
                    {
                        "fields":[
                            {
                                "keyType":"PRIMARY_KEY",
                                "name":"id",
                                "type":4,
                                "value":3
                            },
                            {
                                "keyType":"NULL",
                                "name":"count",
                                "type":4,
                                "value":70
                            }
                        ]
                    }
                ],
                "tableName":"stock_tbl"
            },
            "beforeImage":{
                "rows":[
                    {
                        "fields":[
                            {
                                "keyType":"PRIMARY_KEY",
                                "name":"id",
                                "type":4,
                                "value":3
                            },
                            {
                                "keyType":"NULL",
                                "name":"count",
                                "type":4,
                                "value":100
                            }
                        ]
                    }
                ],
                "tableName":"stock_tbl"
            },
            "sqlType":"UPDATE",
            "tableName":"stock_tbl"
        }
    ],
    "xid":"192.168.51.102:8091:2828558179596595550"
}

5.2 UndoLogManager

UndoLogManager 负责 undo log 的新加、删除、回滚操作,不同的数据库有不同的实现(不同数据库的 SQL 语法会不同),公共逻辑放在了 AbstractUndoLogManager 抽象类中,整体的类继承关系如下图:

3f8c3e44f46174f74f89dd10a123e8df.png

注:图片建议在 PC 端查看

插入和删除 undo log 的逻辑都比较简单,直接操作数据表就行。这里重点看下回滚 undo log 的逻辑:

1289ba71a019aa5858fc215219d399a1.png

源码分析如下:

@Override
public void undo(DataSourceProxy dataSourceProxy, String xid, long branchId) throws TransactionException {
  Connection conn = null;b
    ResultSet rs = null;
  PreparedStatement selectPST = null;
  boolean originalAutoCommit = true;


  for (; ; ) {
    try {
      conn = dataSourceProxy.getPlainConnection();


      // The entire undo process should run in a local transaction.
      // 开启本地事务,确保删除undo log和恢复业务数据的SQL在一个事务中commit
      if (originalAutoCommit = conn.getAutoCommit()) {
        conn.setAutoCommit(false);
      }


      // Find UNDO LOG
      selectPST = conn.prepareStatement(SELECT_UNDO_LOG_SQL);
      selectPST.setLong(1, branchId);
      selectPST.setString(2, xid);
      // 查出branchId的所有undo log记录,用来恢复业务数据
      rs = selectPST.executeQuery();


      boolean exists = false;
      while (rs.next()) {
        exists = true;


        // It is possible that the server repeatedly sends a rollback request to roll back
        // the same branch transaction to multiple processes,
        // ensuring that only the undo_log in the normal state is processed.
        int state = rs.getInt(ClientTableColumnsName.UNDO_LOG_LOG_STATUS);
        // 如果state=1,说明可以回滚;state=1说明不能回滚
        if (!canUndo(state)) {
          if (LOGGER.isInfoEnabled()) {
            LOGGER.info("xid {} branch {}, ignore {} undo_log", xid, branchId, state);
          }
          return;
        }


        String contextString = rs.getString(ClientTableColumnsName.UNDO_LOG_CONTEXT);
        Map<String, String> context = parseContext(contextString);
        byte[] rollbackInfo = getRollbackInfo(rs);


        String serializer = context == null ? null : context.get(UndoLogConstants.SERIALIZER_KEY);
        // 根据serializer获取序列化工具类
        UndoLogParser parser = serializer == null ? UndoLogParserFactory.getInstance()
          : UndoLogParserFactory.getInstance(serializer);
        // 反序列化undo log,得到业务记录修改前后的明文
        BranchUndoLog branchUndoLog = parser.decode(rollbackInfo);


        try {
          // put serializer name to local
          setCurrentSerializer(parser.getName());
          List<SQLUndoLog> sqlUndoLogs = branchUndoLog.getSqlUndoLogs();
          if (sqlUndoLogs.size() > 1) {
            Collections.reverse(sqlUndoLogs);
          }
          for (SQLUndoLog sqlUndoLog : sqlUndoLogs) {
            TableMeta tableMeta = TableMetaCacheFactory.getTableMetaCache(dataSourceProxy.getDbType()).getTableMeta(
                conn, sqlUndoLog.getTableName(), dataSourceProxy.getResourceId());
            sqlUndoLog.setTableMeta(tableMeta);
            AbstractUndoExecutor undoExecutor = UndoExecutorFactory.getUndoExecutor(
                dataSourceProxy.getDbType(), sqlUndoLog);
            undoExecutor.executeOn(conn);
          }
        } finally {
          // remove serializer name
          removeCurrentSerializer();
        }
      }


      // If undo_log exists, it means that the branch transaction has completed the first phase,
      // we can directly roll back and clean the undo_log
      // Otherwise, it indicates that there is an exception in the branch transaction,
      // causing undo_log not to be written to the database.
      // For example, the business processing timeout, the global transaction is the initiator rolls back.
      // To ensure data consistency, we can insert an undo_log with GlobalFinished state
      // to prevent the local transaction of the first phase of other programs from being correctly submitted.
      // See https://github.com/seata/seata/issues/489


      if (exists) {
        deleteUndoLog(xid, branchId, conn);
        conn.commit();
        if (LOGGER.isInfoEnabled()) {
          LOGGER.info("xid {} branch {}, undo_log deleted with {}", xid, branchId,
              State.GlobalFinished.name());
        }
      } else {
        // 如果不存在undo log,可能是因为分支事务还未执行完成(比如,分支事务执行超时),TM发起了回滚全局事务的请求。
        // 这个时候,往undo_log表插入一条记录,可以使分支事务提交的时候失败(undo log)
        insertUndoLogWithGlobalFinished(xid, branchId, UndoLogParserFactory.getInstance(), conn);
        conn.commit();
        if (LOGGER.isInfoEnabled()) {
          LOGGER.info("xid {} branch {}, undo_log added with {}", xid, branchId,
              State.GlobalFinished.name());
        }
      }


      return;
    } catch (SQLIntegrityConstraintViolationException e) {
      // Possible undo_log has been inserted into the database by other processes, retrying rollback undo_log
      if (LOGGER.isInfoEnabled()) {
        LOGGER.info("xid {} branch {}, undo_log inserted, retry rollback", xid, branchId);
      }
    } catch (Throwable e) {
      if (conn != null) {
        try {
          conn.rollback();
        } catch (SQLException rollbackEx) {
          LOGGER.warn("Failed to close JDBC resource while undo ... ", rollbackEx);
        }
      }
      throw new BranchTransactionException(BranchRollbackFailed_Retriable, String
          .format("Branch session rollback failed and try again later xid = %s branchId = %s %s", xid,
            branchId, e.getMessage()), e);
    } finally {
      try {
        if (rs != null) {
          rs.close();
        }
        if (selectPST != null) {
          selectPST.close();
        }
        if (conn != null) {
          if (originalAutoCommit) {
            conn.setAutoCommit(true);
          }
          conn.close();
        }
      } catch (SQLException closeEx) {
        LOGGER.warn("Failed to close JDBC resource while undo ... ", closeEx);
      }
    }
  }
}

备注:需要特别注意下,当回滚的时候,发现 undo log 不存在,需要往 undo_log 表新加一条记录,避免因为 RM 在 TM 发出回滚请求后,又成功提交分支事务的场景。

5.3 Compressor 压缩算法

Compressor 接口定义了压缩算法的规范,用来压缩文本,节省存储空间:

public interface Compressor {


    /**
     * compress byte[] to byte[].
     * @param bytes the bytes
     * @return the byte[]
     */
    byte[] compress(byte[] bytes);


    /**
     * decompress byte[] to byte[].
     * @param bytes the bytes
     * @return the byte[]
     */
    byte[] decompress(byte[] bytes);


}

目前已经实现的压缩算法有如下这些:

a076bcaddddb5854c045dd470e19a477.png

5.4 UndoLogParser 序列化算法

Serializer 接口定义了序列化算法的规范,用来序列化代码:

public interface UndoLogParser {


    /**
     * Get the name of parser;
     * 
     * @return the name of parser
     */
    String getName();


    /**
     * Get default context of this parser
     * 
     * @return the default content if undo log is empty
     */
    byte[] getDefaultContent();


    /**
     * Encode branch undo log to byte array.
     *
     * @param branchUndoLog the branch undo log
     * @return the byte array
     */
    byte[] encode(BranchUndoLog branchUndoLog);


    /**
     * Decode byte array to branch undo log.
     *
     * @param bytes the byte array
     * @return the branch undo log
     */
    BranchUndoLog decode(byte[] bytes);
}

目前已经实现的序列化算法有如下这些:

22f26da4f145159ed2e75baa1811ed0f.png

5.5 Executor 执行器

Executor 是 SQL 执行的入口类, AT 在执行 SQL 前后,需要管理 undo log 的 image 记录,主要是构建 undo log ,包括根据不同的业务 SQL ,来组装查询 undo log 的 SQL 语句;执行查询 undo log 的 SQL ,获取到镜像记录数据;执行插入 undo log 的逻辑(未提交事务)。

public interface Executor<T> {


    /**
     * Execute t.
     *
     * @param args the args
     * @return the t
     * @throws Throwable the throwable
     */
    T execute(Object... args) throws Throwable;
}

针对不同的业务 SQL ,有不同的 Executor 实现,主要是因为不同操作/不同数据库类型的业务 SQL ,生成 undo log 的 SQL 的逻辑不同,所以都分别重写了 beforeImage() 和 afterImage() 方法。整体的继承关系如下图所示:

e31f3ab9c8d68cfa5da644df27cd0a66.png

8dd677f9ef4116cde872fae4bbc42797.png

注:图片建议在 PC 端查看

为了直观地看到不同类型的 SQL 生成的 before image SQL 和 after iamge SQL ,这里做个梳理。假如目标数据表的结构如下:

CREATE TABLE `t` (
  `id` int(64) NOT NULL AUTO_INCREMENT,
  `name` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT "",
  `addr` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT "",
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci


INSERT INTO `t` (`id`, `name`, `addr`) VALUES (1, 'Tom', 'Beijing');
INSERT INTO `t` (`id`, `name`, `addr`) VALUES (2, 'Jack', 'Nanjing');

7ae04d5fd5dfae8456052bc6ce8a5b5d.png

注:图片建议在 PC 端查看

5.6 AsyncWorker

AsyncWorker 是用来做异步执行的,用来做分支事务提交和 undo log 记录删除等操作。

acec5275b7f29bc1ecbae5d3b3276a0a.png

6、关于性能

并不存在某一种完美的分布式事务机制可以适应所有场景,完美满足所有需求。无论 AT 模式、TCC 模式还是 Saga 模式,本质上都是对 XA 规范在各种场景下安全性或者性能的不足的改进。Seata 不同的事务模式是在一致性、可靠性、易用性、性能四个特性之间进行不同的取舍。

近期  Seata 社区发现有同行,在未详细分析 Java 版本 AT 模式的代码的详细实现的情况下,仅对某个早期的 Go 版本的 Seata 进行短链接压测后,质疑 AT 模型的性能及其数据安全性,请具有一定思辨能力的用户朋友们在接受这个结论前仔细查阅其测试方法与测试对象,区分好 “李鬼” 与 “李逵”。

实际上,这个早期的 Go 版本实现仅参照了 Seata v1.4.0,且未严格把 Seata AT 模式的所有功能都予以实现。话说回来,即便其推崇的 Seata XA 模式,其也依赖于单 DB 的XA 模式。而当下最新版本的 MySQL XA 事务模式的 BUG 依然很多,这个地基并没有其想象中的那样百分百稳固。

由阿里与蚂蚁集团共建的 Seata,是我们多年内部分布式事务工程实践与技术经验的结晶,开源出来后得到了多达 150+ 以上行业同行生产环境的验证。开源大道既长且宽,这个道路上可以有机动车道也有非机动车道,还可以有人行道,大家携手把道路拓宽延长,而非站在人行道上宣传机动车道危险性高且车速慢。

7、总结

Seata AT 模式依赖于各个 DB 厂商的不同版本的 DB Driver(数据库驱动),每种数据库发布新版本后,其 SQL 语义及其使用模式都可能发生改变。随着近年 Seata 被其用户们广泛应用于多种业务场景,在开发者们的努力下,Seata AT 模式保持了编程接口与其 XA 模式几乎一致,适配了几乎所有的主流数据库,并覆盖了这些数据库的主要流行版本的 Driver:真正做到了把分布式系统的 “复杂性”留在了框架层面,把易用性和高性能交给了用户。

当然,Seata Java 版本的 XA 和 AT 模式还有许多需要完善与改进的地方,遑论其它多语言版本的实现。欢迎对 Seata 及其多语言版本建设感兴趣的同行参与到 Seata 的建设中来,共同努力把 Seata 打造成一个标准化分布式事务平台。

   本周推荐阅读  

a34eb000f39609ada9d3c9c7d6fbf03c.png

Go内存泄漏,pprof 够用了么?

8e79dd0467225f014646ae6bccecae86.png

Go 原生插件使用问题全解析

339e56a480212f2781654e2d0a7be4c9.png

Go 代码城市上云--KusionStack 实践

7ef66b7606b47fb296490c1ed7bb6462.png

Seata-php 半年规划

21a6a077896d23457282f641f7297f4d.jpeg

Seata-彻底解决Spring Cloud中的分布式事务问题 提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录前言一、pandas是什么?二、使用步骤1.引入库2.读入数据总结 前言 提示:这里可以添加本文要记录的大概内容: 例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容。 提示:以下是本篇文章正文内容,下面案例可供参考 一、pandas是什么? 示例:pandas 是基于NumPy 的一种工具,该工具是为了解决数据分析任务而创建的。 二、使用步骤 1.引入库 代码 阅读详情

相关推荐

动手实践Seata四种模式(XA、AT、TCC、SAGA)

XA 规范 是 X/Open 组织定义的分布式事务处理(DTP,Distributed Transaction Processing)标准,XA 规范 描述了全局的TM与局部的RM之间的接口,几乎所有主流的数据库都对 XA 规范 提供了支持。seata的XA模式做了一些调整,但大体相似:RM一阶段的工作:TC二阶段的工作:RM二阶段的工作:修改application.yml文件(每个参与事务的微服务),开启XA模式: 给发起全局事务的入口方法添加@GlobalTransactional注解,本例中是O

我要记录学习博客 6958

记录seata做分布式事务时出现多线程同时开启分布式事务出现的错误

遇到的问题就是:在做秒杀系统的过程中,使用了async异步处理秒杀请求,并自定义了线程池,然后使用seata保证分布式事务事务完整性,但是在jemeter测试的过程中出现了以下的错误: org.springframework.jdbc.UncategorizedSQLException: ### Error updating database. Cause: java.sql.SQLException: io.seata.core.exception.TransactionException: R.

qq_41617848的博客 4555

Seata --AT模式+TCC模式

文章目录1.Seata介绍2. Seata AT事务方案2.1 Seata AT基本原理2.2 第一阶段:执行各分支事务2.3 第二阶段:控制全局事务最终提交2.4 第二阶段:控制全局事务最终回滚2.5 Seata AT具体工作机制2.5.1 第一阶段:执行分支事务2.5.2 第二阶段:控制全局事务最终回滚2.5.3 第二阶段:控制全局事务最终提交3.Seata TCC模式3.1 TCC 基本原理3.2 第一阶段 Try3.3 第二阶段 Confirm3.4 第二阶段 Cancel3.5 多个事务并发的情况

weixin_58678891的博客 2251

seata源码AT模式

/** * Undo. * * @param dataSourceProxy the data source proxy * @param xid the xid * @param branchId the branch id * @throws TransactionException the transaction exception */ @Override public vo.

dzhou1991的博客 271

用友U8接口开发全攻略:从EAI到OpenAPI的5种方式详解(附避坑指南)

本文深度解析了用友U8系统接口开发的五种核心方式:EAI、API、OpenAPI、SQL直连与内部CO组件。文章详细对比了各方式的适用场景、优缺点及实战操作,并提供了清晰的选型决策框架与避坑指南,帮助开发者根据具体业务需求选择最高效、最稳定的集成路径。

weixin_29322855的博客 685

最佳解决Maven同一依赖多版本共存问题,重复依赖(同一个jar包,多个版本)-maven-shade-plugin

公司的历史项目在导出功能上引用的是poi-3.12版本,后续再做word模板导出功能时用到了poi-3.15版本,升级完后发现项目的很多导出功能都瘫痪了。用旧版本,新功能无法使用;用新版本,旧功能瘫痪,且整改的工作量比较大。使用maven-shade-plugin,将某个版本的依赖名称进行修改,重新引入即可。

S_White_Island的博客 1万+

seata全局事务回滚前,出现的脏写现象

seata全局事务与本地事务的锁冲突

咖啡煮码 2160

Curve+ 5.0.2:新一代色彩校准工具如何革新印刷行业标准

Curve+ 5.0.2作为新一代色彩校准工具,通过G7+校准算法和SCTV技术革新了印刷行业的色彩管理标准。该工具显著提升色彩精准度,减少打样时间和材料浪费,支持专色处理,实现跨材质色彩一致性,为印刷企业带来效率与质量的全面提升。

weixin_29228781的博客 529

Seata AT模式脏写问题与解决方案详解

优点说明性能好一阶段完成直接提交事务,释放数据库资源读写隔离利用全局锁实现隔离性无代码侵入框架自动完成回滚和提交,只需一个注解AT模式通过全局锁解决脏写问题,但在与非Seata事务并发时可能产生数据不一致,需要人工介入。这是高性能分布式事务在极端情况下的必要妥协。

✨ 欢迎来到【Seal ^_^ 的优快云博客】!✨ 4211

【NIFI】实现HANA->ORACLE数据同步

实现 HANA->ORACLE数据同步。

weixin_43159039的博客 1507

【STM32】R05D电控红外协议的美的空调遥控器

目录一、设计思路一、R05D红外协议原理1.协议手册理解2.验证时序(重点)二、硬件实现1.需要的材料2.对发射模块电路进行修改3.STM32 GPIO选择三、代码实现1.载波38kHZ实现2.R05D时序实现3.调用函数并验证 一、设计思路 通常红外遥控采用NEC传输协议,而美的空调采用的是R05D红外协议(应该是自己设计的协议),因此用一般红外编码发射模块无法直接对空调进行控制。 解决方法:获取R05D协议手册 + 用红外接收管对原有遥控器红外接收进行波形分析。 一、R05D红外协议原理 R05D电控功

weixin_42204837的博客 1万+

Seata之AT模式

Seata之AT模式

硅脂味咖啡的博客 9139

【Go语言实战】(2) Gin+Vue 电子商城

618来!!不用纠结去哪里剁手了!直接自己安排一个商场不香吗??

面向生活编程 4827

【Seata】Seata AT和XA模式联系和区别

文章目录概述一、分布式事务产生得原因:1.1、数据库分库分表1.2应用SOA化二、分布式事务解决方案XA模式三、Seata AT(TXC) 模式3.1基本概念3.2AT模式工作流程3.2.1第一阶段:3.2.2第二阶段:3.2.3:AT模式读写隔离实现方式3.2.4:完整的AT在Seata所制定的事务模式下的模型图3.2.5:Seata AT模式优劣:四、XA 模式 运行在 Seata 定义的事务框架内五:Seata AT与XA的优劣参考 概述 确切来说seata at是xa的实现,并且进行了改进。 一、分

m0_45406092的博客 7968

Raspberry Pi Pico + Inmp441麦克风(Pio模拟I2s)<2>

摘要:本文介绍了在Raspberry Pi Pico上使用PIO(Programmable I/O)接收I2S数字麦克风音频数据并保存为WAV文件的方法。项目采用双PIO状态机设计,一个生成I2S时钟信号(WS+SCK),另一个接收24位左声道数据并转换为16位格式。文章详细说明了接线方式、参数设置、WAV文件头构造、PIO汇编程序实现以及数据转换处理流程。项目在MicroPython环境下实现16kHz采样率、6秒时长录音,具有时序精确、稳定可靠的特性,并提供了进一步拓展的可能性,如双声道支持、实时频谱分

技术需要沉淀,需要碰撞。 660

YOLOv11知识蒸馏,实现无损涨点,包含逻辑蒸馏和特征蒸馏中的不同蒸馏方法,适用专栏内的所有模型

知识蒸馏(Knowledge Distillation)是由 Hinton 等人在 2015 年提出来的,主要是用一个复杂的教师模型去指导一个简单的学生模型训练,这样能让学生模型在保持高精度的同时,还能减少计算量。本文便是讲解如何针对的原始模型以及改进后的模型进行知识蒸馏,轻松实现无损涨点。知识蒸馏的核心在于利用已训练好的复杂模型(教师模型)去指导一个更简单的模型(学生模型)进行训练,以此让学生模型在保留较高精度的同时,降低计算成本。特征蒸馏(Feature Distillation)是知识蒸馏的一种形式,

Limiiiing的博客 4961

Seata AT模式详解:无侵入的高性能分布式事务

AT模式通过"业务SQL立即提交释放锁 + undo-log保证可回滚"的创新设计,在保证最终一致性的前提下,大幅提升了分布式事务的性能,是Seata最核心、最常用的模式。

✨ 欢迎来到【Seal ^_^ 的优快云博客】!✨ 4176

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

Seata的架构以及实现过程详解,seata的XA、TCC、AT模式的一步步搭建和部署,三种模式的特点详解,超详细,掌握seata一篇足矣

我的微服务较为简单,主要是三个板块(订单支付、库存管理,个人账户管理),当完成一条订单插入数据时,如果数据合法,就需要将对应的个人账户的金额和库存进行减少,数据不合法要保证业务的事务性(因为三个模块操作的是不同的数据库表)

weixin_64339548的博客 2150

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

Seata AT模式 vs XA模式:核心区别深度解析

XA模式用资源锁定换强一致,AT模式用快照换高性能。两者都是两阶段提交,但选择了不同的性能与一致性平衡点。

✨ 欢迎来到【Seal ^_^ 的优快云博客】!✨ 4272

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

seata的AT模式原理

Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 为用户提供了 AT、TCC、SAGA 和 XA 事务模式,用户打造一站式的分布式解决方案。Seata是Java领域很强大的分布式事务框架,其支持了多种模式。其中默认支持的AT模式,相比于传统的2PC协议(基于数据库的XA协议),很好地解决了2PC长期锁资源的问题,提高了并发度。Seata支持的各个模式中,AT模式对业务零入侵实现分布式事务,对于开发者更加友好。...

RuanEmpty的博客 5016

Spring Boot项目整合Seata AT模式

Spring Boot项目整合Stata

qq_36551991的博客 3982

Seata四大模式之AT模式详解及代码实现

Seata四大模式之AT模式详解及代码实现

一恍过去 2万+

Seata AT 模式 原理详解

前提 基于支持本地 ACID 事务的关系型数据库。 Java 应用,通过 JDBC 访问数据库。 整体机制 两阶段提交协议的演变: 一阶段:业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。 二阶段: 提交异步化,非常快速地完成。 回滚通过一阶段的回滚日志进行反向补偿。 写隔离 一阶段本地事务提交前,需要确保先拿到全局锁。 拿不到全局锁,不能提交本地事务。 拿全局锁的尝试被限制在一定范围内,超出范围将放弃,并回滚本地事务,释放本地锁。 以...

骏马逸动,心随你动的博客 4095

seata-AT模式

seata-AT模式

程序员码小跳 5171
上一篇: TiDB Hackathon 2022 邀请函 | 探索代码世界里更有趣的事,解锁技术更多可能性
下一篇: Seata AT 模式代码级详解
SOFAStack
博客等级 码龄7年 747粉丝 389原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值