Loading... ## 为什么需要分布式事务 举个例子我们就能很简单的明白了,假如我们现在有一个下单业务的需求,前端请求首先进入订单服务,创建订单并写入数据库,然后订单服务调用账户服务和库存服务,账户服务负责扣减余额,库存服务负责扣减商品库存,但是他们都是独立的事务,无法单一的一个独立事务出现错误后全部都会滚,所以这个时候就需要分布式事务的存在了。 在分布式系统下,如果一个业务跨越多个服务或数据源,每个服务都是一个分支事务,要`保证所有分支事务最终状态一致`,这样的事务就是`分布式事务`。其中的每一个事务都是一个`分支事务`,整个业务称为`全局事务`。 ![业务例子](https://resource.if010.com/SpringCloudSeata_01.png) **解决思路:** 解决分布式事务,各个子事务之间必须能感知到彼此的事务状态,这样才能保证状态的一致性。 ![解决思路图](https://resource.if010.com/SpringCloudSeata_02.png) ## Seata分布式事务架构 Seata是2019年1月蚂蚁金服和阿里巴巴共同开源的分布式事务解决方案,致力于提供高性能和简单的分布式事务服务,为用户打造一站式的分布式解决方案,而目前已经移交给了Apache开源基金会组织,处于孵化中状态。 官网地址:[https://seata.apache.org](https://seata.apache.org),其中的文档、博客中提供了大量的使用说明、源码分析。 ### Steata事务管理中的三个重要角色 **TC (Transaction Coordinator) - 事务协调者:** 维护全局和分支事务的状态,协调全局事务提交或回滚。 **TM (Transaction Manager) - 事务管理器:** 定义全局事务的范围、开始全局事务、提交或回滚全局事务。 **RM (Resource Manager) - 资源管理器:** 管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。 ![Steata事务管理中的三个重要角色关系图](https://resource.if010.com/SpringCloudSeata_03.png) ### TC服务部署 #### 下载Seata安装包 先在[Seata官网](https://seata.apache.org/zh-cn/unversioned/download/seata-server/)下载Seata安装包 ![官网下载信息](https://resource.if010.com/SpringCloudSeata_04.png) #### 数据库初始化 解压下载好的Seata安装包后,可以在`seata-server/script/server/db`目录下看到`dm.sql、mysql.sql oracle.sql、postgresql.sql、sqlserver.sql`数据库脚本,Seata支持的数据源类型还是挺多的,常见的过长达梦数据库、MySQL数据库、微软的SQL Server、甲骨文的Oracle数据库等等 ```sql create database seata character set utf8; create user 'seata'@'%' IDENTIFIED BY '123456'; grant all privileges ON seata.* TO 'seata'@'%'; flush privileges; use seata; source seata-server/script/server/db/mysql.sql ``` #### 修改Seate配置文件 Seate的配置文件在`seata-server/conf`目录下,编辑`application.yml`文件 ```yaml # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # server: # Seata 服务监听端口 port: 7091 spring: application: name: if010-seata # Seata日志记录相关配置 logging: config: classpath:logback-spring.xml file: path: ${log.home:${user.home}/logs/seata} extend: logstash-appender: destination: 127.0.0.1:4560 kafka-appender: bootstrap-servers: 127.0.0.1:9092 topic: logback_to_logstash console: user: # Seata控制台认证用户名称 username: seata # Seata控制台认证用户密码 password: seata seata: # Seata 服务配置 Nacos配置 config: support: nacos, consul, apollo, zk, etcd3 type: nacos nacos: server-addr: 127.0.0.1:8848 username: nacos password: 123456 namespace: group: DEFAULT_GROUP data-id: application-dev-seata.yml # Seata 服务注册配置,支持nacos, eureka, redis, zk, consul, etcd3, sofa registry: type: nacos nacos: server-addr: 127.0.0.1:8848 username: nacos password: 123456 namespace: group: DEFAULT_GROUP data-id: application-dev-seata.yml serviceName: if010-seata # 存储方式 store: mode: db #事务锁信息存储方式 lock: mode: db #事务回话信息存储方式 session: mode: db #db或redis存储密码解密公钥 publicKey: If010#com@yy88.. db: dbType: mysql driverClassName: com.mysql.cj.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true&rewriteBatchedStatements=true user: seata password: Esj6jFczA#Emazlr5 minConn: 5 maxConn: 30 globalTable: global_table branchTable: branch_table distributedLockTable: distributed_lock queryLimit: 100 lockTable: lock_table vgroupTable: vgroup-table maxWait: 5000 server: raft: group: default server-addr: snapshot-interval: 600 apply-batch: 32 max-append-bufferSize: 262144 max-replicator-inflight-msgs: 256 disruptor-buffer-size: 16384 election-timeout-ms: 1000 reporter-enabled: false reporter-initial-delay: 60 serialization: jackson compressor: none sync: true service-port: 8091 max-commit-retry-timeout: -1 max-rollback-retry-timeout: -1 rollback-retry-timeout-unlock-enable: false enable-check-auth: true enable-parallel-request-handle: true enable-parallel-handle-branch: false retry-dead-threshold: 130000 xaer-nota-retry-timeout: 60000 enableParallelRequestHandle: true applicationDataLimitCheck: true applicationDataLimit: 64000 recovery: committing-retry-period: 1000 async-committing-retry-period: 1000 rollbacking-retry-period: 1000 timeout-retry-period: 1000 undo: log-save-days: 7 log-delete-period: 86400000 session: branch-async-queue-size: 5000 enable-branch-async-remove: false metrics: enabled: false registry-type: compact exporter-list: prometheus exporter-prometheus-port: 9898 transport: rpc-tc-request-timeout: 15000 enable-tc-server-batch-send-response: false shutdown: wait: 3 thread-factory: boss-thread-prefix: NettyBoss worker-thread-prefix: NettyServerNIOWorker boss-thread-size: 1 security: secretKey: SeataSecretKey0c382ef121d778043159209298fd40bf3850a017 tokenValidityInMilliseconds: 1800000 csrf-ignore-urls: /metadata/v1/** ignore: urls: /,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.jpeg,/**/*.ico,/api/v1/auth/login,/version.json,/health,/error,/vgroup/v1/** ``` > 注意: > > 1、如果配置了使用Nacos则会使用Nacos上的配置优先,就算下面配置了存储方式,Nacos没有配置的话还是会使用默认的file存储,存在Bug。 > > 2、由于许可证兼容性问题,Seata无法在分发包中包含 mysql、mariadb、oracle 等 jar 依赖项。所以我们需要手动的下载MySQL的驱动jar包,将数据库驱动程序依赖项(例如 `mysql-connector-java.jar`)复制到`seata-server/lib/jdbc`目录下,但是如果你用的是File文件存储方式则无需理会。 #### 启动服务 启动服务的脚本目录存放在了`seata-server/bin`,直接执行`./seata-server.sh`,如果需要停止服务的话可以执行`./seata-server.sh stop`,启动后我们可以通过`nohup.out`查看启动情况 ```xml ███████╗███████╗ █████╗ ████████╗ █████╗ ██╔════╝██╔════╝██╔══██╗╚══██╔══╝██╔══██╗ ███████╗█████╗ ███████║ ██║ ███████║ ╚════██║██╔══╝ ██╔══██║ ██║ ██╔══██║ ███████║███████╗██║ ██║ ██║ ██║ ██║ ╚══════╝╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ 09:33:14.062 INFO --- [main] [der$InnerEnhancedServiceLoader] [lExtensionDefinition] [] : Ignore load compatible class io.seata.config.ExtConfigurationProvider, because is not assignable from origin type org.apache.seata.config.ExtConfigurationProvider 09:33:14.288 INFO --- [main] [ta.config.ConfigurationFactory] [ load] [] : load Configuration from :Spring Configuration 09:33:14.325 INFO --- [main] [seata.server.ServerApplication] [ logStarting] [] : Starting ServerApplication v2.2.0 using Java 11.0.24 on seata.if010.com with PID 1912 09:33:14.326 INFO --- [main] [seata.server.ServerApplication] [ogStartupProfileInfo] [] : No active profile set, falling back to 1 default profile: "default" 09:33:16.022 INFO --- [main] [mbedded.tomcat.TomcatWebServer] [ initialize] [] : Tomcat initialized with port(s): 7091 (http) 09:33:16.032 INFO --- [main] [oyote.http11.Http11NioProtocol] [ log] [] : Initializing ProtocolHandler ["http-nio-7091"] 09:33:16.033 INFO --- [main] [.catalina.core.StandardService] [ log] [] : Starting service [Tomcat] 09:33:16.033 INFO --- [main] [e.catalina.core.StandardEngine] [ log] [] : Starting Servlet engine: [Apache Tomcat/9.0.90] 09:33:16.136 INFO --- [main] [rBase.[Tomcat].[localhost].[/]] [ log] [] : Initializing Spring embedded WebApplicationContext 09:33:16.136 INFO --- [main] [letWebServerApplicationContext] [ebApplicationContext] [] : Root WebApplicationContext: initialization completed in 1765 ms 09:33:16.548 ERROR --- [main] [.db.AbstractDataSourceProvider] [ getPassword] [] : decryption failed,please confirm whether the ciphertext and secret key are correct! error msg: Illegal base64 character 23 09:33:16.777 INFO --- [main] [vlet.WelcomePageHandlerMapping] [ <init>] [] : Adding welcome page: class path resource [static/index.html] 09:33:17.194 INFO --- [main] [oyote.http11.Http11NioProtocol] [ log] [] : Starting ProtocolHandler ["http-nio-8847"] 09:33:17.213 INFO --- [main] [mbedded.tomcat.TomcatWebServer] [ start] [] : Tomcat started on port(s): 8847 (http) with context path '' 09:33:17.231 INFO --- [main] [seata.server.ServerApplication] [ logStarted] [] : Started ServerApplication in 4.306 seconds (JVM running for 5.192) 09:33:17.384 INFO --- [main] [rver.lock.LockerManagerFactory] [ init] [] : use lock store mode: db 09:33:17.405 INFO --- [main] [a.server.session.SessionHolder] [ init] [] : use session store mode: db 09:33:17.905 INFO --- [main] [aba.druid.pool.DruidDataSource] [ init] [] : {dataSource-1} inited 09:33:18.076 INFO --- [main] [rpc.netty.NettyServerBootstrap] [ start] [] : Server started, service listen port: 7091 09:33:18.094 INFO --- [main] [nacos.NacosRegistryServiceImpl] [tNacosAuthProperties] [] : Nacos check auth with userName/password. 09:33:18.096 INFO --- [main] [nacos.NacosRegistryServiceImpl] [tNacosAuthProperties] [] : Nacos check auth with userName/password. ``` > 关于Seata的启动脚本`seata-server.sh`参数说明 > > > | 参数 | 全写 | 作用 | 备注 | > | ------ | -------------- | ---------------------------- | ----------------------------------------------------------------------------- | > | -h | --host | 指定在注册中心注册的 IP | 不指定时获取当前的 IP,外部访问部署在云环境和容器中的 server 建议指定 | > | -p | --port | 指定 server 启动的端口 | 默认为 8091 | > | -m | --storeMode | 事务日志存储方式 | 支持`file`,`db`,`redis`,默认为 `file` 注:redis需seata-server 1.3版本及以上 | > | -n | --serverNode | 用于指定seata-server节点ID | 如`1`,`2`,`3`..., 默认为 `1` | > | -e | --seataEnv | 指定 seata-server 运行环境 | 如`dev`, `test` 等, 服务启动时会使用 `registry-dev.conf` 这样的配置 | #### 访问控制台 访问`http://localhost:7091` ![Seata控制台](https://resource.if010.com/SpringCloudSeata_05.png) ### 微服务集成Seata #### 引入依赖 ```xml <!-- SpringCloud Seata 依赖包 --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-seata</artifactId> </dependency> ``` #### 微服务Seata配置 ```yaml seata: # TC服务注册中心的配置,微服务根据这些信息去注册中心获取TC服务地址 registry: # 注册中心类型 type: nacos nacos: # Seata注册Nacos地址 server-addr: 127.0.0.1:8848 # Seata所在Nacos中注册的命名空间 namespace: "" # Seata所在Nacos中注册的分组名称 group: DEFAULT_GROUP # Seata服务注册名称 application: seata-server username: nacos password: 123456 # 事务组名称 tx-service-group: if010-cloud service: # 事务组与tc集群的映射关系 vgroup-mapping: if010-cloud: "default" ``` ### Seata的事务模式 Seata提供了四种事务模式,分别为 AT、TCC、SAGA 和 XA。 #### XA模式 XA 模式是从 1.2 版本支持的事务模式。XA 规范 是 X/Open 组织定义的分布式事务处理(DTP,Distributed Transaction Processing)标准,XA规范描述了全局的TM与局部的RM之间的接口,几乎所有主流的数据库都对XA规范提供了支持。 ![XA模式流程图](https://resource.if010.com/SpringCloudSeata_06.png) Seata的XA模式流程详解: * RM一阶段的工作 `注册分支事务到TC` -> `执行分支业务sql但不提交` -> `报告执行状态到TC` * TC二阶段的工作 `TC检测各分支事务执行状态` -> `如果都成功,通知所有RM提交事务` -> `如果有失败,通知所有RM回滚事务` * RM二阶段的工作 `接收TC指令,提交或回滚事务` ##### XA模式的优势 * 事务的强一致性,满足ACID原则。 * 常用数据库都是支持的,实现简单,并且没有代码入侵 ##### XA模式的缺点 * 因为一阶段需要锁定数据库的资源,等待二阶段结束才会释放,性能较差 * 依赖关系型数据库实现事务 ##### 实现XA模式 Seata的starter已经完成了XA模式的自动装配,实现非常简单,步骤如下: 1、修改application.yml文件(每个参与事务的微服务),开启XA模式: ```yaml seata: # 开启数据源代理的XA模式 data-source-proxy-mode: XA ``` 2、给发起全局事务的入口方法添加`@GlobalTransactional`注解,本例中是一个订单创建扣减库存的场景模拟: ```java @Override @GlobalTransactional public Long create(Order order) { // 创建订单 orderMapper.insert(order); // 扣余额 bannerMapper.update(order); // 扣减库存 boxMapper.delete(order); return order.getId(); } @Transactional public Long insert(Order order) { // 创建订单的方法 // 尝试创建,失败抛出异常 } @Transactional public Long update(Order order) { // 扣减余额的方法 // 尝试扣除,失败抛出异常 } @Transactional public Long delete(Order order) { // 扣减库存的方法 // 尝试扣除,扣除失败抛出异常 } ``` 3、重启服务并测试 #### AT模式 [官方推荐模式] Seata主推的是AT模式,AT模式同样是分阶段提交的事务模型,不过弥补了XA模型中资源锁定周期过长的缺陷。 ![AT模式流程图](https://resource.if010.com/SpringCloudSeata_07.png) * 阶段一RM的工作 `注册分支事务` -> `记录undo-log(数据快照)` -> `执行业务sql并提交` -> `报告事务状态` * 阶段二提交时RM的工作 `删除undo-log即可` * 阶段二回滚时RM的工作 `根据undo-log恢复数据到更新前` ##### AT模式的优势 * 一阶段完成直接提交事务,释放数据库资源,性能比较好 * 利用全局锁实现读写隔离 * 没有代码侵入,框架自动完成回滚和提交 ##### AT模式的缺点 * 两阶段之间属于软状态,属于最终一致 * 框架的快照功能会影响性能,但比XA模式要好很多 ##### 实现AT模式 1、在每个微服务的数据库中添加AT模式`undo_log`数据表 ```sql -- 注意此处0.3.0+ 增加唯一索引 ux_undo_log CREATE TABLE `undo_log` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID,自增字段', `branch_id` bigint(20) NOT NULL COMMENT '分支事务ID', `xid` varchar(100) NOT NULL COMMENT '全局事务ID', `context` varchar(128) NOT NULL COMMENT 'undo_log context, such as serialization', `rollback_info` longblob NOT NULL COMMENT '回滚信息', `log_status` int(11) NOT NULL COMMENT '日志状态, (0:normal | 1:defense)', `log_created` datetime NOT NULL COMMENT '创建时间', `log_modified` datetime NOT NULL COMMENT '变更时间', `ext` varchar(100) DEFAULT NULL COMMENT '拓展字段', PRIMARY KEY (`id`), UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT '分布式数据记录表'; ``` 2、修改application.yml文件(每个参与事务的微服务),开启TA模式: ```yaml seata: # 开启数据源代理的AT模式 data-source-proxy-mode: AT ``` #### TCC模式 TCC模式与AT模式非常相似,每阶段都是独立事务,不同的是TCC通过人工编码来实现数据恢复。 需要实现三个方法,每个方法也就是对应的每一个阶段: **Try:** 资源的检测和预留 **Confirm:** 完成资源操作业务;要求 Try 成功 Confirm 一定要能成功 **Cancel:** 预留资源释放,可以理解为try的反向操作 ![TCC模式流程图](https://resource.if010.com/SpringCloudSeata_08.png) TCC模式原理 举个栗子,一个扣减用户余额的业务。假设账户A原来余额是100,需要余额扣减30元 阶段一:`Try` 检查余额是否充足,如果充足则冻结金额增加30元,可用余额扣除30 ![](https://resource.if010.com/SpringCloudSeata_10.png) 阶段二:假如要提交`Confirm`,则冻结金额扣减30 ![](https://resource.if010.com/SpringCloudSeata_11.png) 阶段二:如果要回滚`Cancel`,则冻结金额扣减30,可用余额增加30 ![](https://resource.if010.com/SpringCloudSeata_12.png) ##### TCC模式的优势 * 一阶段完成直接提交事务,释放数据库资源,性能好 * 相比AT模型,无需生成快照,无需使用全局锁,性能最强 * 不依赖数据库事务,而是依赖补偿操作,可以用于非事务型数据库 ##### TCC模式的缺点 * 有代码侵入,需要人为编写try、Confirm和Cancel接口,太麻烦 * 软状态,事务是最终一致 * 需要考虑Confirm和Cancel的失败情况,做好幂等处理 #### Saga模式 Saga模式是SEATA提供的长事务解决方案。 ![Saga模式流程图](https://resource.if010.com/SpringCloudSeata_09.png) 阶段一:直接提交本地事务 阶段二:成功则什么都不做;失败则通过编写补偿业务来回滚 ##### Saga模式的优势 * 事务参与者可以基于事件驱动实现异步调用,吞吐高 * 阶段一直接提交事务,无锁,性能好 * 不用编写TCC中的三个阶段,实现简单 ##### Saga模式的缺点 * 软状态持续时间不确定,时效性差 * 没有锁,没有事务隔离,会有脏写 #### 四种模式对比 | <br/> | XA | AT | TCC | SAGA | | ---------- | -------------------------------------- | ---------------------------------------------------- | ------------------------------------------------------------ | --------------------------------------------------------------------------------------------------- | | 一致性 | 强一致 | 弱一致 | 弱一致 | 最终一致 | | 隔离性 | 完全隔离 | 基于全局锁隔离 | 基于资源预留隔离 | 无隔离 | | 代码侵入 | 无 | 无 | 有,要编写三个接口 | 有,要编写状态机和补偿业务 | | 性能 | 差 | 好 | 非常好 | 非常好 | | 场景 | 对一致性、隔离性<br />有高要求的业务 | 基于关系型数据库的大多数<br />分布式事务场景都可以 | 对性能要求较高的事务。<br />有非关系型数据库要参与的事务。 | 业务流程长、业务流程多参与者包含<br />其它公司或遗留系统服务,无法提供TCC模式<br />要求的三个接口 | 其实,解决分布式事务的最优选择是尽量的不要分布式事务,尽量的避免分布式事务,真正的企业开发当中,是非常少的会使用到分布式事务,更多的是消息通知的方式 ## 补充 ### 事务的ACID原则 ![事务的ACID原则](https://resource.if010.com/SpringCloudSeata_13.png) ### 关于CAP定理 1998年,加州大学的计算机科学家 Eric Brewer 提出,分布式系统有三个指标: * **Consistency (一致性):** 用户访问分布式系统中的任意节点,得到的数据必须一致 * **Availability (可用性):** 用户访问集群中的任意健康节点,必须能得到响应,而不是超时或拒绝 * **Partition tolerance (分区容错性):** * **Partition (分区):** 因为网络故障或其它原因导致分布式系统中的部分节点与其它节点失去连接,形成独立分区。 * **Tolerance (容错):** 在集群出现分区时,整个系统也要持续对外提供服务 Eric Brewer 说,分布式系统无法同时满足这三个指标。这个结论就叫做 CAP 定理。 ![CAP定理图](https://resource.if010.com/SpringCloudSeata_14.png) 分布式系统节点通过网络连接,一定会出现分区问题(P),当分区出现时,系统的一致性(C)和可用性(A)就无法同时满足。 ### BASE理论 BASE理论是对CAP的一种解决思路,包含三个思想: * **Basically Available (基本可用):** 分布式系统在出现故障时,允许损失部分可用性,即保证核心可用。 * **Soft State (软状态):** 在一定时间内,允许出现中间状态,比如临时的不一致状态。 * **Eventually Consistent (最终一致性):** 虽然无法保证强一致性,但是在软状态结束后,最终达到数据一致。 ### 解决分布式事务的思想和模型 分布式事务最大的问题是各个子事务的一致性问题,因此可以借鉴CAP定理和BASE理论: * **AP模式:** 各子事务分别执行和提交,允许出现结果不一致,然后采用弥补措施恢复数据即可,实现最终一致。 * **CP模式:** 各个子事务执行后互相等待,同时提交,同时回滚,达成强一致。但事务等待过程中,处于弱可用状态。 **全局事务:** 整个分布式事务 **分支事务:** 分布式事务中包含的每个子系统的事务 **最终一致思想:** 各分支事务分别执行并提交,如果有不一致的情况,再想办法恢复数据 **强一致思想:** 各分支事务执行完业务不要提交,等待彼此结果。而后统一提交或回滚 ### 关于AT模式的脏写问题 为什么会产生脏写? 我们举个栗子,现在有两个全局事务,分别是 事务1 和 事务二,分别对 余额表的 money 字段进行更新操作,money 的初始值 100。 事务1 先开始,开启本地事务,拿到DB锁,更新操作 `money= 100 - 10 = 90`,操作结束提交。 事务2 后开始,开启本地事务,拿到DB锁,更新操作 `money= 90 - 10 = 80`,操作结束提交。 此时此刻,事务1 发现分支事务执行错误需要回滚快照,开启本地事务,拿到DB锁,更新操作 `money= 100`,操作结束提交。 现在我们发现数据错乱了,这就是脏数据产生的场景,尽管发生的可能性非常的低。。。。 ![AT模式的脏写问题产生原因](https://resource.if010.com/SpringCloudSeata_15.png) 那么Seata的AT模式是如何解决的呢? 还是刚才的那个栗子,有两个全局事务,分别是 事务1 和 事务二,分别对 余额表的 money 字段进行更新操作,money 的初始值 100。 事务1 先开始,开启本地事务,拿到本地锁,更新操作 `money = 100 - 10 = 90`。在本地事务提交前,先拿到该记录的 全局锁 ,本地提交释放本地锁。 事务2 后开始,开启本地事务,拿到本地锁,更新操作 `money = 90 - 10 = 80`。本地事务提交前,尝试拿该记录的 全局锁 ,但是 事务1 全局提交前,该记录的全局锁被 事务1 持有,而 事务2 需要重试等待 全局锁 。 此时此刻,事务1 发现分支事务执行错误需要回滚快照,开启本地事务,但是 事务1 需要拿到DB锁才能进行更新操作 回滚操作,而DB锁又被 事务2 持有还没释放,这就是死锁的产生原因。 但是无需担心,事务2 在尝试获取全局锁无果后会认为执行失败,主动的回滚并释放DB锁。 当 事务2 的DB锁释放后,事务1 就可以拿到DB锁了,这样就可以正常回滚快照数据。 ![AT模式的脏写问题解决方案-写隔离方案(同)](https://resource.if010.com/SpringCloudSeata_16.png) 上述的解决方案是建立在相同的分布式事务当中,归属于同一个Seata管理,那么如果别的事务并不在同一个Seata管理呢? Seata的AT模式保存快照是保存了两份快照,一份是更改前的数据快照,另一份是一份更改后的数据快照,当 事务1 回滚前会对当前数据进行一个比对,如果数据存在差异则会记录异常,发送警告通知人工介入处理。 ![AT模式的脏写问题解决方案-写隔离方案(非)](https://resource.if010.com/SpringCloudSeata_17.png) 最后修改:2024 年 11 月 13 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 -