Seata 分布式事务的精简使用教程和原理浅析

  • 一、说明
  • 二、Seata 简介
    • 2.1、Seata 是什么?
    • 2.2、Seata 的整体架构
      • 2.2.1、主要角色
      • 2.2.2、整体架构和工作流程图
    • 2.3、Seata 的事务模式
      • 2.3.1、Seata 的 AT 事务模式
    • 2.4、微服务框架支持
      • 2.4.1、事务上下文
      • 2.4.2、事务传播
    • 2.5、ORM 框架支持
    • 2.6、数据库类型支持
    • 2.7、SQL 参考
  • 三、Seata 通用接入流程
    • 3.1、Seata Server 端(TC)
    • 3.2、Seata Client 端(TM和RM)
      • 3.2.1、业务系统集成 Seata Client
      • 3.2.2、不同微服务框架解决 Seata 事务传播问题
        • 3.2.2.1、事务传播的原理
        • 3.2.2.2、事务传播的解决方案
        • 3.2.2.3、常用微服务框架的事务传播问题
          • (1)、Seata + Dubbo 分布式事务
          • (2)、Seata + Spring Cloud OpenFeign 分布式事务
          • (3)、Seata + RestTemplate 分布式事务
          • (3)、Seata + 其他微服务框架
  • 四、Seata 分布式事务使用演示
  • 五、Seata 工作原理简单分析
    • 5.1、工作流程
    • 5.2、主要的类

一、说明

  • 本博客首先会对 Seata 进行简单介绍,然后演示如何在项目中使用 Seata 分布式事务(仅 AT 模式),并结合监控工具简单分析它的工作原理,最后再谈谈博主在工作中关于 Seata 的使用感受。

    • Seata 简介

      • 介绍一些 Seata 的重要概念,和胖友们在一些事情上达成共识。
    • Seata 通用接入流程
      • 这部分内容也属于通用知识,和具体工作环境无关。
      • 介绍 Server 端和 Client 端的使用,尤其是 Client 端使用不同微服务框架时如何解决事务传播问题。
    • Seata 分布式事务使用演示
      • 可以看做是上面 “Seata 通用接入流程” 的实战版。
      • 由于博主目前工作的公司使用的是 SpringBoot 1.x 版本,在一些配置细节上可能和某些胖友不同,但万变不离其宗,只要能理解它的核心思想,相信这些小细节问题对于各位优秀的胖友们都是小意思。
    • Seata 工作原理简单分析
      • 首先会提供通过监控工具得到的工作流程截图,让大家有个直观的认知。
      • 然后介绍代码中关键的类。
  • 胖友们可以把本博客当成是一个快速上手的参考手册,但如果想要全面系统地了解 Seata 框架,建议查看 Seata 的官方文档以及 GitHub
    • 官方文档传送阵
    • GitHub 传送阵

二、Seata 简介

看到博主这篇博客的胖友,想必大多都是从业 n 年的同仁,所以一些基础概念的介绍和铺垫这里就省略了,而只介绍博主认为最核心的部分。

2.1、Seata 是什么?

  • Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。

2.2、Seata 的整体架构

2.2.1、主要角色

  • TC (Transaction Coordinator) - 事务协调者

    • 维护全局和分支事务的状态,驱动全局事务提交或回滚。
  • TM (Transaction Manager) - 事务管理器
    • 定义全局事务的范围:开始全局事务、提交或回滚全局事务。
  • RM (Resource Manager) - 资源管理器
    • 管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。
  • 事务回话信息存储
    • 非必须角色,理论上可以不引入额外组件。
    • 事务会话信息存储方式有:file本地文件(不支持HA),db数据库|redis(支持HA)
    • 但从生产实践角度来看,这个组件也是必须的,博主使用的是 MySQL
  • 注册中心
    • 非必须角色,理论上可以不引入额外组件。
    • 默认file,支持file 、nacos 、eureka、redis、zk、consul、etcd3、sofa、custom
    • 但从生产实践角度来看,这个组件也是必须的,博主使用的是 zk.
  • 配置中心
    • 非必须角色,理论上可以不引入额外组件。
    • 默认file,支持file、nacos 、apollo、zk、consul、etcd3、custom
    • 从生产实践角度来看,这个组件也是必须的,博主使用的是 zk.

2.2.2、整体架构和工作流程图

  • 整体架构图

  • 工作流程图

2.3、Seata 的事务模式

  • AT
  • TCC
  • SAGA
  • XA 事务模式

2.3.1、Seata 的 AT 事务模式

  • 前提

    • 基于支持本地 ACID 事务的关系型数据库。
    • Java 应用,通过 JDBC 访问数据库。
  • 整体机制
    • 两阶段提交协议的演变:

      • 一阶段:业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。
      • 二阶段:
        • 提交异步化,非常快速地完成。
        • 回滚通过一阶段的回滚日志进行反向补偿。
  • 写隔离
    • 一阶段本地事务提交前,需要确保先拿到 全局锁 。
    • 拿不到 全局锁 ,不能提交本地事务。
    • 拿 全局锁 的尝试被限制在一定范围内,超出范围将放弃,并回滚本地事务,释放本地锁
  • 读隔离
    • 在数据库本地事务隔离级别 读已提交(Read Committed) 或以上的基础上,Seata(AT 模式)的默认全局隔离级别是 读未提交(Read Uncommitted)
    • 如果应用在特定场景下,必需要求全局的 读已提交 ,目前 Seata 的方式是通过 SELECT FOR UPDATE 语句的代理。

2.4、微服务框架支持

  • 从上面的 2.2.2、整体架构和工作流程图 的工作流程图中可以看到,整个事务过程中,需要有一个全局的事务ID,并且需要把这个全局事务ID在各个微服务间传播。

2.4.1、事务上下文

  • Seata 的事务上下文由 RootContext 来管理。
  • 应用开启一个全局事务后,RootContext 会自动绑定该事务的 XID,事务结束(提交或回滚完成),RootContext 会自动解绑 XID。
  • 应用可以通过 RootContext 的 API 接口(RootContext.getXID())来获取当前运行时的全局事务 XID
  • 应用是否运行在一个全局事务的上下文中,就是通过 RootContext 是否绑定 XID 来判定的

2.4.2、事务传播

  • 服务内部的事务传播

    • 默认的,RootContext 的实现是基于 ThreadLocal 的,即 XID 绑定在当前线程上下文中
    • 所以服务内部的 XID 传播通常是天然的通过同一个线程的调用链路串连起来的。默认不做任何处理,事务的上下文就是传播下去的
    • 如果希望挂起事务上下文,则需要通过 RootContext 提供的 API 来实现:
      •   // 挂起(暂停)String xid = RootContext.unbind();// TODO: 运行在全局事务外的业务逻辑// 恢复全局事务上下文RootContext.bind(xid);
        
  • 跨服务调用的事务传播
    • 跨服务调用场景下的事务传播,本质上就是要把 XID 通过服务调用传递到服务提供方,并绑定到 RootContext 中去。
    • 只要能做到这点,理论上 Seata 可以支持任意的微服务框架。

2.5、ORM 框架支持

  • Seata 虽然是保证数据一致性的组件,但对于 ORM 框架并没有特殊的要求,像主流的Mybatis,Mybatis-Plus,Spring Data JPA, Hibernate等都支持。这是因为ORM框架位于JDBC结构的上层,而 Seata 的 AT,XA 事务模式是对 JDBC 标准接口操作的拦截和增强

2.6、数据库类型支持

  • AT模式支持的数据库有:MySQL、Oracle、PostgreSQL和 TiDB
  • TCC模式不依赖数据源(1.4.2版本)

2.7、SQL 参考

  • 完整 SQL 参考传送阵
  • 需要特别注意它的使用限制:
    • 不支持 SQL 嵌套
    • 不支持多表复杂 SQL
    • 不支持存储过程、触发器
    • 不支持批量更新 SQL

三、Seata 通用接入流程

3.1、Seata Server 端(TC)

  1. 下载程序包并解压

    1. 当前最新版本:seata-server-1.4.2
  2. 配置事务回话信息存储方式
    1. 配置文件:seata-server-1.4.2\conf\file.conf
    2. 事务会话信息存储方式有:file本地文件(不支持HA),db数据库|redis(支持HA),这里配置为 db.
      1. ## transaction log store, only used in seata-server
        store {## store mode: file、db、redis
        mode = "db"
        ## rsa decryption public key
        publicKey = ""## database store propertydb {## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp)/HikariDataSource(hikari)  etc.datasource = "druid"## mysql/oracle/postgresql/h2/oceanbase etc.dbType = "mysql"driverClassName = "com.mysql.cj.jdbc.Driver"## if using mysql to store the data, recommend add rewriteBatchedStatements=true in jdbc connection paramurl = "jdbc:mysql://127.0.0.1:3306/seata?serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true"user = "root"password = "Andy6666"minConn = 5maxConn = 100globalTable = "global_table"branchTable = "branch_table"lockTable = "lock_table"queryLimit = 100maxWait = 5000}}
    3. 在 MySQL 数据库创建事务回话信息表
      1. -- -------------------------------- The script used when storeMode is 'db' --------------------------------
        -- the table to store GlobalSession data
        CREATE TABLE IF NOT EXISTS `global_table`
        (`xid`                       VARCHAR(128) NOT NULL,`transaction_id`            BIGINT,`status`                    TINYINT      NOT NULL,`application_id`            VARCHAR(32),`transaction_service_group` VARCHAR(32),`transaction_name`          VARCHAR(128),`timeout`                   INT,`begin_time`                BIGINT,`application_data`          VARCHAR(2000),`gmt_create`                DATETIME,`gmt_modified`              DATETIME,PRIMARY KEY (`xid`),KEY `idx_gmt_modified_status` (`gmt_modified`, `status`),KEY `idx_transaction_id` (`transaction_id`)
        ) ENGINE = InnoDB
        DEFAULT CHARSET = utf8;-- the table to store BranchSession data
        CREATE TABLE IF NOT EXISTS `branch_table`
        (`branch_id`         BIGINT       NOT NULL,`xid`               VARCHAR(128) NOT NULL,`transaction_id`    BIGINT,`resource_group_id` VARCHAR(32),`resource_id`       VARCHAR(256),`branch_type`       VARCHAR(8),`status`            TINYINT,`client_id`         VARCHAR(64),`application_data`  VARCHAR(2000),`gmt_create`        DATETIME(6),`gmt_modified`      DATETIME(6),PRIMARY KEY (`branch_id`),KEY `idx_xid` (`xid`)
        ) ENGINE = InnoDB
        DEFAULT CHARSET = utf8;-- the table to store lock data
        CREATE TABLE IF NOT EXISTS `lock_table`
        (`row_key`        VARCHAR(128) NOT NULL,`xid`            VARCHAR(128),`transaction_id` BIGINT,`branch_id`      BIGINT       NOT NULL,`resource_id`    VARCHAR(256),`table_name`     VARCHAR(32),`pk`             VARCHAR(36),`gmt_create`     DATETIME,`gmt_modified`   DATETIME,PRIMARY KEY (`row_key`),KEY `idx_branch_id` (`branch_id`)
        ) ENGINE = InnoDB
        DEFAULT CHARSET = utf8;
  3. 配置注册中心和配置中心
    1. 配置文件:seata-server-1.4.2\conf\registry.conf。这里博主把注册中心和配置中心都设置为 zk(胖友们可以根据情况设置成其他的,配置大同小异)。

      1.  registry {# file 、nacos 、eureka、redis、zk、consul、etcd3、sofatype = "zk"zk {cluster = "default"serverAddr = "127.0.0.1:2181"sessionTimeout = 6000connectTimeout = 2000username = ""password = ""}}config {# file、nacos 、apollo、zk、consul、etcd3type = "zk"zk {cluster = "default"serverAddr = "127.0.0.1:2181"sessionTimeout = 6000connectTimeout = 2000username = ""password = ""}}
  4. 启动 Server 端
    1. 点击 seata-server-1.4.2\bin\seata-server.bat(或 seata-server.sh)

3.2、Seata Client 端(TM和RM)

3.2.1、业务系统集成 Seata Client

  1. 业务数据库创建回滚日志表

    1.  -- for AT mode you must to init this sql for you business database. the seata server not need it.CREATE TABLE IF NOT EXISTS `undo_log`(`branch_id`     BIGINT       NOT NULL COMMENT 'branch transaction id',`xid`           VARCHAR(128) NOT NULL COMMENT 'global transaction id',`context`       VARCHAR(128) NOT NULL COMMENT 'undo_log context,such as serialization',`rollback_info` LONGBLOB     NOT NULL COMMENT 'rollback info',`log_status`    INT(11)      NOT NULL COMMENT '0:normal status,1:defense status',`log_created`   DATETIME(6)  NOT NULL COMMENT 'create datetime',`log_modified`  DATETIME(6)  NOT NULL COMMENT 'modify datetime',UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)) ENGINE = InnoDBAUTO_INCREMENT = 1DEFAULT CHARSET = utf8 COMMENT ='AT transaction mode undo table';
  2. 业务系统添加 seata 依赖
    1.  <dependency><groupId>io.seata</groupId><artifactId>seata-spring-boot-starter</artifactId><version>1.3.0</version></dependency>
      
  3. 业务系统配置文件中配置注册中心和配置中心、事务分组名称
    1. 这里注册中心和配置中心都选择 zk(以 account-service 为例)
    2.  seata:registry:type: zkzk:server-addr: localhost:2181config:type: zkzk:server-addr: localhost:2181txServiceGroup:  account-service-g
      
  4. 在 Seata 配置中心配置事务分组和TC集群的关联关系
    1. 在 zk 添加以下配置(其他配置中心大同小异):

      1. 节点名称:/seata/service.vgroupMapping.account-service-g,节点内容:default

        1. 其中 account-service-g 是事务分组名称
        2. default 是 TC 集群名称
        3. 即配置所有事务分组名称为 account-service-g 的 TM 和 RM 注册到集群名称为 default 的 TC 集群

3.2.2、不同微服务框架解决 Seata 事务传播问题

3.2.2.1、事务传播的原理

  • 2.4.2、事务传播 可以知道,跨服务调用场景下的事务传播,本质上就是要把 XID 通过服务调用传递到服务提供方,并绑定到 RootContext 中去。

3.2.2.2、事务传播的解决方案

  • 远程服务调用方:

    • 发起远程服务调用时,需要把全局事务XID包含到请求信息中
  • 远程服务提供方:
    • 处理请求前,解析获取 XID 并绑定到 RootContext 中
    • 处理请求后,将 XID 从 RootContext 中解绑

3.2.2.3、常用微服务框架的事务传播问题

(1)、Seata + Dubbo 分布式事务
  • 如何实现 Dubbo 请求的前置和后置处理呢? 熟悉 Dubbo 的胖友可能就会说,扩展它的 Filter。
  • 没错,我们就是要扩展 Dubbo 的 Filter,在服务调用方发起请求时,设置全局事务 XID;在服务提供方处理请求前,解析获取XID,并绑定到 RootContext,处理完请求后,清理 XID。
  • 幸运的是,Seata 框架中已经默认提供了这样的 Filter,它基于 SPI 机制自动注册,项目中也可以通过 ServiceLoader.load 查看。
  • 也就是说,Seata 天然支持 Dubbo 框架的事务传播,我们什么都不需要做。
(2)、Seata + Spring Cloud OpenFeign 分布式事务
  • Spring Cloud OpenFeign 是基于 Http 协议的微服务框架。远程服务调用方需要在 Header 中设置全局事务XID;远程服务提供方在处理请求前需要从 Http Header 中获取全局事务XID 并绑定到 RootContext,处理完请求后清理 XID。
  • 推荐的做法:
    • 远程服务调用方:通过 feign 提供的 RequestInterceptor 设置 Header。但由于 OpenFeign 默认集成了 Hystrix,通信时使用的是异步多线程通信,所以还需要自定义配置 HystrixConcurrencyStrategy,处理线程间全局事务XID传递的问题。

      • 配置 HystrixConcurrencyStrategy:

        •   @Slf4j@Configurationpublic class FeignHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy {private HystrixConcurrencyStrategy delegate;public FeignHystrixConcurrencyStrategy() {try {this.delegate = HystrixPlugins.getInstance().getConcurrencyStrategy();if (this.delegate instanceof FeignHystrixConcurrencyStrategy) {return;}HystrixCommandExecutionHook commandExecutionHook = HystrixPlugins.getInstance().getCommandExecutionHook();HystrixEventNotifier eventNotifier = HystrixPlugins.getInstance().getEventNotifier();HystrixMetricsPublisher metricsPublisher = HystrixPlugins.getInstance().getMetricsPublisher();HystrixPropertiesStrategy propertiesStrategy = HystrixPlugins.getInstance().getPropertiesStrategy();HystrixPlugins.reset();HystrixPlugins.getInstance().registerConcurrencyStrategy(this);HystrixPlugins.getInstance().registerCommandExecutionHook(commandExecutionHook);HystrixPlugins.getInstance().registerEventNotifier(eventNotifier);HystrixPlugins.getInstance().registerMetricsPublisher(metricsPublisher);HystrixPlugins.getInstance().registerPropertiesStrategy(propertiesStrategy);}catch (Exception ex) {log.error("Failed to register Seata Hystrix Concurrency Strategy", ex);}}@Overridepublic <T> Callable<T> wrapCallable(Callable<T> callable) {if (callable instanceof SeataContextCallable) {return callable;}return new SeataContextCallable<>(callable,RequestContextHolder.getRequestAttributes());}private static class SeataContextCallable<K> implements Callable<K> {private final Callable<K> actual;private final String xid;private final RequestAttributes requestAttributes;SeataContextCallable(Callable<K> actual, RequestAttributes requestAttribute) {this.actual = actual;this.requestAttributes = requestAttribute;this.xid = RootContext.getXID();}@Overridepublic K call() throws Exception {try {RequestContextHolder.setRequestAttributes(requestAttributes);if (!StringUtils.isEmpty(xid)) {RootContext.bind(xid);}return actual.call();}finally {if (!StringUtils.isEmpty(xid)) {RootContext.unbind();}RequestContextHolder.resetRequestAttributes();}}}}
          
      • 添加 feign RequestInterceptor 设置 Header
        •   @Slf4jpublic class FeignBasicAuthRequestInterceptor implements RequestInterceptor {@Overridepublic void apply(RequestTemplate template) {try {// 支持 seata 事务传播String xid = RootContext.getXID();if (!StringUtils.isEmpty(xid)) {template.header(RootContext.KEY_XID, xid);}} catch (Exception e) {log.error("FeignBasicAuthRequestInterceptor apply fail。", e);}}}
    • 远程服务调用方:基于 WEB 拦截器做前置、后置处理。例如,基于 Spring Web 项目,可以通过 HandlerInterceptor 实现:
      •   public class SeataHandlerInterceptor implements HandlerInterceptor {private static final Logger log = LoggerFactory.getLogger(SeataHandlerInterceptor.class);@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response,Object handler) {String xid = RootContext.getXID();String rpcXid = request.getHeader(RootContext.KEY_XID);if (log.isDebugEnabled()) {log.debug("xid in RootContext {} xid in RpcContext {}", xid, rpcXid);}if (StringUtils.isBlank(xid) && rpcXid != null) {RootContext.bind(rpcXid);if (log.isDebugEnabled()) {log.debug("bind {} to RootContext", rpcXid);}}return true;}@Overridepublic void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response,Object handler, Exception e) {if (StringUtils.isNotBlank(RootContext.getXID())) {String rpcXid = request.getHeader(RootContext.KEY_XID);if (StringUtils.isEmpty(rpcXid)) {return;}String unbindXid = RootContext.unbind();if (log.isDebugEnabled()) {log.debug("unbind {} from RootContext", unbindXid);}if (!rpcXid.equalsIgnoreCase(unbindXid)) {log.warn("xid in change during RPC from {} to {}", rpcXid, unbindXid);if (unbindXid != null) {RootContext.bind(unbindXid);log.warn("bind {} back to RootContext", unbindXid);}}}}}
        
(3)、Seata + RestTemplate 分布式事务
  • RestTemplate 同样基于 Http 协议,处理方式和 Spring Cloud OpenFeign 类似。推荐的做法:

    • 远程服务调用方:

      • 定义拦截器:

        •   public class SeataRestTemplateInterceptor implements ClientHttpRequestInterceptor {@Overridepublic ClientHttpResponse intercept(HttpRequest httpRequest, byte[] bytes,ClientHttpRequestExecution clientHttpRequestExecution) throws IOException {HttpRequestWrapper requestWrapper = new HttpRequestWrapper(httpRequest);String xid = RootContext.getXID();if (!StringUtils.isEmpty(xid)) {requestWrapper.getHeaders().add(RootContext.KEY_XID, xid);}return clientHttpRequestExecution.execute(requestWrapper, bytes);}}
          
      • 给 RestTemplate 设置拦截器
        • restTemplate.setInterceptors(Collections.singletonList(new SeataRestTemplateInterceptor()));
    • 远程服务提供方:基于 WEB 拦截器做前置、后置处理。处理方式完全和 Spring Cloud OpenFeign一样,即可以复用上面的 SeataHandlerInterceptor
(3)、Seata + 其他微服务框架
  • 实现思路参考:3.2.2.2、事务传播的解决方案
  • 在动手实现前,先查看 seata 包中是否已经默认提供相关方案。例如,motan、grpc 框架也享受和 Dubbo 框架同样的待遇,Seata 同样也默认提供了一个拥有传播事务XID的过滤器。
  • 实在找不到现成方案,我想根据上面的思路,自己实现一套事务传播方案,相信对于各位能看到这里的胖友来说,应该是小意思。

四、Seata 分布式事务使用演示

  • 创建 3 个项目,order-service、storage-service、account-service

  • order-service 开启事务,并通过 Spring Cloud OpenFeign 调用 storage-service 服务。

  • storage-service 通过 RestTemplate 调用 account-service 提供的服务。

  • account-service 提供的服务。

  • 通过 postman 调用 order-service 的接口,可以分别看到以下日志:

  • 通过日志,可以看到全局事务最终是提交的状态。而要使全局事务的回滚,只需要让其中一个分支事务失败即可,这里就不演示了。

五、Seata 工作原理简单分析

5.1、工作流程

这里使用 SkyWalking 进行链路追踪,给大家展示 四、Seata 分布式事务使用演示 的完整流程

  1. 完整链路追踪:

  2. 对 Postman 请求的链路追踪

    1. order-service

    2. storage-service

    3. account-service

  3. 全局事务提交后,异步删除 undo_log

5.2、主要的类

  • 2.5、ORM 框架支持 ,我们知道:

    • Seata 虽然是保证数据一致性的组件,但对于 ORM 框架并没有特殊的要求,像主流的Mybatis,Mybatis-Plus,Spring Data JPA, Hibernate等都支持。这是因为ORM框架位于JDBC结构的上层,而 Seata 的 AT,XA 事务模式是对 JDBC 标准接口操作的拦截和增强。
  • Seata 是如何对 JDBC 标准接口操作的拦截和增强???答案在 DataSourceProxy。
    • 它对 DataSource 进行了增强
    • 它的 getConnection 方法返回的是:ConnectionProxy
      • ConnectionProxy 对 Connection 进行了增强
      • 它的 prepareStatement 方法返回的是同样是一个 Proxy 代理类
  • 从 DataSource 开始,Seata 对 JDBC 的标准接口进行了层层增强。而具体每一个增强类实现做了哪些事情,本博客就不深入探讨了。
  • 已经有不少关于 DataSourceProxy 源码解读的博客,各位胖友们可以移步观看。

Seata 分布式事务的使用和原理浅析相关推荐

  1. Spring Cloud Alibaba 综合应用:Seata 分布式事务实战教程

    在前面的章节,我们分别讲解了 Spring Cloud Alibaba 中主要的组成部分,其中包括:注册中心与配置管理组件 Nacos.Ribbon 客户端负载均衡.OpenFeign 与 Dubbo ...

  2. SpringCloud分布式事务,版本二:添加 Seata 分布式事务版本

    基于 Seata 1.4.0 版本 首先贴出此项目地址:Seata 分布式事务版本 先了未添加事务项目再看此版本:未添加事务版本 此文章是基于上一篇的项目基础上添加的内容,所以务必先看上一篇 Seat ...

  3. 全面剖析Seata 分布式事务 AT 与XA

    前言 昨天有小伙伴私信小编说想小编出一期Seata分布式事物XA与AT模式的解析,经过昨晚的熬夜加班整理,今天将为大家带来Seata 分布式事务 XA 与 AT 的全面剖析.文章分为:XA模式是什么? ...

  4. Seata分布式事务XA与AT全面解析

    Seata 分布式事务 XA与AT Seata 是一款开源的分布式事务解决方案,star高达17300+,社区活跃度极高,致力于在微服务架构下提供高性能和简单易用的分布式事务服务. 注:本期分享借鉴于 ...

  5. seata分布式事务回滚机制是如何实现的

    本文来说下seata分布式事务回滚机制是如何实现的 文章目录 概述 概述

  6. seata分布式事务协调管理器是如何实现的

    本文来说下seata分布式事务协调管理器是如何实现的 文章目录 概述 概述

  7. seata分布式事务一致性锁机制如何实现的

    本文来说下seata分布式事务一致性锁机制是如何实现的 文章目录 概述 概述

  8. 关于seata分布式事务框架的几个问题

    本文来说下关于seata分布式事务框架的几个问题 文章目录 概述 Seata 分布式事务解决方案比较 本文小结 概述 seata是两阶段提交事务,第一阶段解析业务sql并且生成对应快照,第二阶段是提交 ...

  9. SpringCloud微服务实战——搭建企业级开发框架(二十七):集成多数据源+Seata分布式事务+读写分离+分库分表

      读写分离:为了确保数据库产品的稳定性,很多数据库拥有双机热备功能.也就是,第一台数据库服务器,是对外提供增删改业务的生产服务器:第二台数据库服务器,主要进行读的操作.   目前有多种方式实现读写分 ...

最新文章

  1. 通过RMAN克隆11g数据库(基于active database)
  2. python爬虫教程视频下载-利用Python网络爬虫获取电影天堂视频下载链接【详细教程】...
  3. [渝粤教育] 中国地质大学 企业文化建设与管理 复习题
  4. 打开是什么样子的图片_情侣头像 | 无论是什么样子的你 我都好喜欢
  5. java集合框架类_Java集合框架总结—超详细-适合面试
  6. java静态类如何赋值_Java class对象说明 Java 静态变量声明和赋值说明
  7. 【转】QDockWidget 停靠窗口和工具栏
  8. libpcre.so.1 cannot be found
  9. 查看其他计算机的共享资源,NetResView (共享资源查看)
  10. 数据库索引的使用及优化
  11. QT5基础教程(介绍,下载,安装,第一个QT程序)
  12. 18-(基础入门篇)GPRS(Air202)拨打电话
  13. panabit之PPPoE认证
  14. Pyspark特征工程--RFormula
  15. 基于WiFi的Android局域网即时通讯软件——Android源码
  16. 2017年学习总结-新的开始
  17. springBoot thymeleaf 属性为空时报错:EL1007E
  18. thinkpad重装系统不引导_联想电脑为什么重装win7系统后引导不了
  19. 蓝桥杯 算法训练 旅行家的预算
  20. 三菱PLC FX5U与FX5U 直间modbus tcp 通讯

热门文章

  1. bui框架与php结合,吐槽一下BUI的那些坑
  2. 有些人控制欲望真tmd强,想杀人!
  3. 腾讯游戏学院专家:UE高级性能剖析技术之RHI
  4. ZZULIOJ-1007,鸡兔同笼(Java)
  5. 乐观锁CAS的简单理解
  6. PyQt6案例3:简单计算器案例
  7. 记录h5表单离开页面时数据储存及取出的问题
  8. unity GPU优化
  9. 使用ESP8266组网
  10. Pycharm一键生成代码块