Seata 分布式事务的使用和原理浅析
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 框架,建议查看 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)
- 下载程序包并解压
- 当前最新版本:seata-server-1.4.2
- 配置事务回话信息存储方式
- 配置文件:
seata-server-1.4.2\conf\file.conf
- 事务会话信息存储方式有:file本地文件(不支持HA),db数据库|redis(支持HA),这里配置为 db.
## 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}}
- 在 MySQL 数据库创建事务回话信息表
-- -------------------------------- 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;
- 配置文件:
- 配置注册中心和配置中心
- 配置文件:
seata-server-1.4.2\conf\registry.conf
。这里博主把注册中心和配置中心都设置为 zk(胖友们可以根据情况设置成其他的,配置大同小异)。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 = ""}}
- 配置文件:
- 启动 Server 端
- 点击 seata-server-1.4.2\bin\seata-server.bat(或 seata-server.sh)
3.2、Seata Client 端(TM和RM)
3.2.1、业务系统集成 Seata Client
- 业务数据库创建回滚日志表
-- 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';
- 业务系统添加 seata 依赖
<dependency><groupId>io.seata</groupId><artifactId>seata-spring-boot-starter</artifactId><version>1.3.0</version></dependency>
- 业务系统配置文件中配置注册中心和配置中心、事务分组名称
- 这里注册中心和配置中心都选择 zk(以 account-service 为例)
seata:registry:type: zkzk:server-addr: localhost:2181config:type: zkzk:server-addr: localhost:2181txServiceGroup: account-service-g
- 在 Seata 配置中心配置事务分组和TC集群的关联关系
- 在 zk 添加以下配置(其他配置中心大同小异):
- 节点名称:/seata/service.vgroupMapping.account-service-g,节点内容:default
- 其中 account-service-g 是事务分组名称
- default 是 TC 集群名称
- 即配置所有事务分组名称为 account-service-g 的 TM 和 RM 注册到集群名称为 default 的 TC 集群
- 节点名称:/seata/service.vgroupMapping.account-service-g,节点内容:default
- 在 zk 添加以下配置(其他配置中心大同小异):
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);}}}
- 配置 HystrixConcurrencyStrategy:
- 远程服务调用方:基于 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);}}}}}
- 远程服务调用方:通过 feign 提供的 RequestInterceptor 设置 Header。但由于 OpenFeign 默认集成了 Hystrix,通信时使用的是异步多线程通信,所以还需要自定义配置 HystrixConcurrencyStrategy,处理线程间全局事务XID传递的问题。
(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 分布式事务使用演示 的完整流程
完整链路追踪:
对 Postman 请求的链路追踪
order-service
storage-service
account-service
全局事务提交后,异步删除 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 分布式事务的使用和原理浅析相关推荐
- Spring Cloud Alibaba 综合应用:Seata 分布式事务实战教程
在前面的章节,我们分别讲解了 Spring Cloud Alibaba 中主要的组成部分,其中包括:注册中心与配置管理组件 Nacos.Ribbon 客户端负载均衡.OpenFeign 与 Dubbo ...
- SpringCloud分布式事务,版本二:添加 Seata 分布式事务版本
基于 Seata 1.4.0 版本 首先贴出此项目地址:Seata 分布式事务版本 先了未添加事务项目再看此版本:未添加事务版本 此文章是基于上一篇的项目基础上添加的内容,所以务必先看上一篇 Seat ...
- 全面剖析Seata 分布式事务 AT 与XA
前言 昨天有小伙伴私信小编说想小编出一期Seata分布式事物XA与AT模式的解析,经过昨晚的熬夜加班整理,今天将为大家带来Seata 分布式事务 XA 与 AT 的全面剖析.文章分为:XA模式是什么? ...
- Seata分布式事务XA与AT全面解析
Seata 分布式事务 XA与AT Seata 是一款开源的分布式事务解决方案,star高达17300+,社区活跃度极高,致力于在微服务架构下提供高性能和简单易用的分布式事务服务. 注:本期分享借鉴于 ...
- seata分布式事务回滚机制是如何实现的
本文来说下seata分布式事务回滚机制是如何实现的 文章目录 概述 概述
- seata分布式事务协调管理器是如何实现的
本文来说下seata分布式事务协调管理器是如何实现的 文章目录 概述 概述
- seata分布式事务一致性锁机制如何实现的
本文来说下seata分布式事务一致性锁机制是如何实现的 文章目录 概述 概述
- 关于seata分布式事务框架的几个问题
本文来说下关于seata分布式事务框架的几个问题 文章目录 概述 Seata 分布式事务解决方案比较 本文小结 概述 seata是两阶段提交事务,第一阶段解析业务sql并且生成对应快照,第二阶段是提交 ...
- SpringCloud微服务实战——搭建企业级开发框架(二十七):集成多数据源+Seata分布式事务+读写分离+分库分表
读写分离:为了确保数据库产品的稳定性,很多数据库拥有双机热备功能.也就是,第一台数据库服务器,是对外提供增删改业务的生产服务器:第二台数据库服务器,主要进行读的操作. 目前有多种方式实现读写分 ...
最新文章
- 通过RMAN克隆11g数据库(基于active database)
- python爬虫教程视频下载-利用Python网络爬虫获取电影天堂视频下载链接【详细教程】...
- [渝粤教育] 中国地质大学 企业文化建设与管理 复习题
- 打开是什么样子的图片_情侣头像 | 无论是什么样子的你 我都好喜欢
- java集合框架类_Java集合框架总结—超详细-适合面试
- java静态类如何赋值_Java class对象说明 Java 静态变量声明和赋值说明
- 【转】QDockWidget 停靠窗口和工具栏
- libpcre.so.1 cannot be found
- 查看其他计算机的共享资源,NetResView (共享资源查看)
- 数据库索引的使用及优化
- QT5基础教程(介绍,下载,安装,第一个QT程序)
- 18-(基础入门篇)GPRS(Air202)拨打电话
- panabit之PPPoE认证
- Pyspark特征工程--RFormula
- 基于WiFi的Android局域网即时通讯软件——Android源码
- 2017年学习总结-新的开始
- springBoot thymeleaf 属性为空时报错:EL1007E
- thinkpad重装系统不引导_联想电脑为什么重装win7系统后引导不了
- 蓝桥杯 算法训练 旅行家的预算
- 三菱PLC FX5U与FX5U 直间modbus tcp 通讯