分布式事务组件:Seata
一、事务的概念
指的是一个操作单元、在这个操作单元中的所有操作最终要保持一致的行为、要么全都成功、要么全都失败。常见的场景在于银行的转入转出、电商项目的下单与库存之间货物信息等等。
二、事务的特性
原子性(Atomicity):操作这些指令时,要么全部执行成功,要么全部不执行。只要其中一个指令执行失败,所有的指令都执行失败,数据进行回滚,回到执行指令前的数据状态。要么执行,要么不执行
一致性(Consistency):事务的执行使数据从一个状态转换为另一个状态,数据库的完整性约束没有被破坏。能量守恒,总量不变
eg: 拿转账来说,假设用户A和用户B两者的钱加起来一共是2000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还得是2000,这就是事务的一致性。
隔离性(Isolation):隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。信息彼此独立,互不干扰
即要达到这么一种效果:对于任意两个并发的事务T1和T2,在事务T1看来,T2要么在T1开始之前就已经结束,要么在T1结束之后才开始,这样每个事务都感觉不到有其他事务在并发地执行。****
持久性(Durability):当事务正确完成后,它对于数据的改变是永久性的。接下来的其他操作或故障不应该对本次事务的修改有任何影响。
三、什么是分布式事务:
随着互联网的快速发展、软件系统由原来的单体应用转变为分布式应用。下午描述了单体应用像微服务的演变:分布式系统会把一个应用系统拆分为可独立部署的多个服务、因此需要服务与服务之间的远程协作才能完成事务操作。这种分布式系统环境下由不同的服务之间通过网路远程协作完成事务称之分布式事务。 体现在业务上: 订单系统完成后、库存系统减少。银行存取款业务。
四、分布式事务主要业务上问题
分布式事务实现
begin transaction;
//1.本地数据库操作:张三减少金额
//2.远程调用:让李四增加金额
commit transation;
当远程调用让李四增加金额成功了、由于网络问题远程调用并没有返回、此时本地事务提交失败就回滚了张三减少金额的操作、此时张三和李四的数据就不一致了。因此在分布式架构的基础上、传统数据库是事务就无法使用了。
张三和李四的账户不在一个数据库中甚至不在一个应用系统里,实现转账事务需要通过远程调用、由于网络问题就会导致分布式事务问题。网络因素成为了分布式事务的考量标准之一。
五、分布式事务基础理论 :CAP、BASE
-
5.1、 CAP原则 CAP原则又称CAP定理、同时又被称作布鲁尔定理(Brewer’s theorem)指的是在一个分布式系统中、不可能同时满足以下三点。
- 一致性(Consistency) : 指强一致性,在写操作完成后开始的任何读操作都必须返回该值。或者后续操作的结果。 通俗的讲就是客户端写入服务器数据并得到了响应、那么客户端下次从任何服务器读取的数据都是同步后的数据。
- 可用性(Availablility): 可用性是指,每次像未崩溃的节点发送请求,总能保证收到相应数据 (允许不是最新数据) 和一致性的差别在于、不管节点是否挂掉、都会进行响应。
- 分区容忍性(Partition Tolerance) : 分布式系统6在遇到任何网络分区故障的时候、仍然能够对外提供满足一致性和可用性的服务。也就是说,A和B可能因为各种意外情况,导致无法成功进行同步, 服务器A和B发送给对方的任何消息都是可以放弃的。分布式系统要能容忍这种情况。
-
5.2、CAP原则不能同时满足三种要求、为什么要在AC之间取舍呢?
组合 | 分析结果 |
---|
CA | 满足一致性和可用性、放弃分区容错性。单体架构类型 | CP | 满足分区容错性和一致性。放弃可用性。当两节点分区出现故障断开,那么将对客户端进行阻断、不能收到响应。(应用场景:阿里电商。 ZooKeeper / Redis / MongoDB / HBase) | AP | 满足可用性和分区容错性。意外发生后、节点仍对外服务、可能会发送同步前的响应。(应用场景:12306。 显示有票、点击购买后票没了。CoachDB / Cassandra / DynamoDB) |
结论:保证了一致性、则必须同步不同节点的数据。同步期间则锁定操作,违背可用性。 保证了可用性,那么不能锁定同步期间的操作,也就破坏了一致性。 对于一个分布式系统来说,CAP三者中,P是基本要求、只能通过基础设施提升,无法通过降低C/A来提升。一个有效的策略是:保证可用性、舍弃强一致性,但保证最终一致性。比如一些高并发的站点(秒杀、淘宝、12306)。 最终接近兼顾三个特性。
?
? 结论:BASE理论是对CAP中的一致性和可用性进行一个权衡的结果,理论的核心思想就是:我们无法做到强一致,但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性
六、分布式事务协议
背景:
在分布式系统里、每个节点都可以知道自己操作的成功或者失败、却无法知道其他节点的操作成功失败。当事务跨多个节点时,为了保持事务的原子性与一致性、而引入一个协调者来统一掌控所有参与者的操作结果、并指示他们是否要把操作结果进行真正的提交或者回滚。
二阶段提交 (2PC)
? 二阶段提交协议(Two-phase Commit,即 2PC)是常用的分布式事务解决方案,即将事务的提交过程分为两个阶段来进行处理。
二阶段提交优缺点:
? 优点: 尽量保证了数据的强一致性,适合对数据强一致要求很高的关键领域
?
? 缺点:
? 1.性能问题: 执行过程,所有参与节点都是事务阻塞型的. 当参与者占用公共资源时,其他第三方节点访问公共资源不得不处于阻塞状态.
? 2,可靠性问题: 参与者发生故障. 协调者需要给每个参与者额外指定超时机制.超时后整个事务失败. 协调者发生故障. 参与者会一致阻塞下去.需要额外的备机进行容错.
? 3.数据一致性问题: 二阶段无法解决的问题:协调者发出commit信息之后宕机,而唯一接受到这条消息的参与者同时也宕机了. 那么即使协调者通过选举协议产生了新的协调者.这条事务的状态也是不确定的,没人知道事务是否提交了
总结: 实现复杂,牺牲了可用性,对性能影响较大,不适合高并发高性能场景.
三阶段事务(3PC)
三阶段提交又称3PC,其在两阶段提交的基础上增加了 CanCommit阶段,并引入了 超时机制。一旦事务参与者迟迟没有收到协调者的Commit请求,就会自动进行本地commit,这样相对有效地解决了协调者单点故障的问题。
但是性能问题和不一致问题仍然没有根本解决。下面我们还是一起看下三阶段流程的是什么样的?
第一阶段:CanCommit阶段
这个阶段类似于2PC中的第二个阶段中的Ready阶段,是一种事务询问操作,事务的协调者向所有参与者询问“你们是否可以完成本次事务?”,如果参与者节点认为自身可以完成事务就返回“YES”,否则“NO”。而在实际的场景中参与者节点会对自身逻辑进行事务尝试,其实说白了就是检查下自身状态的健康性,看有没有能力进行事务操作。
第二阶段:PreCommit阶段
在阶段一中,如果所有的参与者都返回Yes的话,那么就会进入PreCommit阶段进行事务预提交。此时分布式事务协调者会向所有的参与者节点发送PreCommit请求,参与者收到后开始执行事务操作,并将Undo和Redo信息记录到事务日志中。参与者执行完事务操作后(此时属于未提交事务的状态),就会向协调者反馈“Ack”表示我已经准备好提交了,并等待协调者的下一步指令。
如果阶段一中有任何一个参与者节点返回的结果是No响应,或者协调者在等待参与者节点反馈的过程中因挂掉而超时(2PC中只有协调者可以超时,参与者没有超时机制)。整个分布式事务就会中断,协调者就会向所有的参与者发送“abort”请求。
第三阶段:DoCommit阶段
在阶段二中如果所有的参与者节点都可以进行PreCommit提交,那么协调者就会从“预提交状态” 转变为 “提交状态”。然后向所有的参与者节点发送"doCommit"请求,参与者节点在收到提交请求后就会各自执行事务提交操作,并向协调者节点反馈“Ack”消息,协调者收到所有参与者的Ack消息后完成事务。
理解:相比较2PC而言,3PC对于协调者(Coordinator)和参与者(Partcipant)都设置了超时时间,而2PC只有协调者才拥有超时机制。这个优化点,主要是避免了参与者在长时间无法与协调者节点通讯(协调者挂掉了)的情况下,无法释放资源的问题,因为参与者自身拥有超时机制会在超时后,自动进行本地commit从而进行释放资源。而这种机制也侧面降低了整个事务的阻塞时间和范围。
七、分布式事务解决方案
1、事务补偿 (TCC)
? TCC方案是一种应用层面侵入业务的两阶段提交。是目前最火的一种柔性事务方案,其核心思想是: 针对每个操作,都要注册一个与其对应的确认和补偿(撤销)操作。
? 根据第一阶段的结果、决定是执行confirm还是cancel
? Confirm(确认): 执行真正的业务,释放锁
? Cancle(取消) : 是预留资源的取消。同样释放锁。
?
八、Seata介绍
GTS简介 [Seata前身、收费版本]
Seata简介
19年,阿里分布式事务框架GTS【Global Transaction Service】开源了一个免费社区版Fescar、在微服务系统中,分布式事务一直是痛点,也是难点。社区里也有一些开源的分布式解决方案的框架,比如ByteTCC、LCN,但是这些框架没有一个权威的组织在维护,或多或少大家都有点不敢用。 于是 Fescar(Seata改名前) 应运而生
Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。
官方站点:
- 源码: https://github.com/seata/seata
- 文档: http://seata.io/zh-cn/index.html
使用情况:
Seata术语:
- TC (Transaction Coordinator) 事务协调者 : 维护全局和分支事务的状态,驱动全局事务提交或回滚。
- TM (Transaction Manager) 事务管理者 : 定义全局事务的范围:开始全局事务、提交或回滚全局事务。
- RM (Resource Manager) 资源管理器 : 管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。
Seata架构图:
? UNDO_LOG表 【回滚日志】:
? 当TM向RM发送RPC(远程调用)时、会在每个业务的数据中生成undo_log这张表。保存回滚数据。
? 当全局提交时、undo_log表会直接删除。 当全局回滚时、会将数据进行撤销。还原至以前的操作状态。
CREATE TABLE `undo_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`branch_id` bigint(20) NOT NULL,
`xid` varchar(100) NOT NULL,
`context` varchar(128) NOT NULL,
`rollback_info` longblob NOT NULL,
`log_status` int(11) NOT NULL,
`log_created` datetime NOT NULL,
`log_modified` datetime NOT NULL,
`ext` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
操作时会保存回滚日志的两条数据 : 前镜像 【操作前的数据记录】、后镜像【操作后的数据记录】 AT模式展开说明。
#Undo_log文件
{
"branchId": 641789253,
"undoItems": [{
"afterImage": {
"rows": [{
"fields": [{
"name": "id",
"type": 4,
"value": 1
}, {
"name": "name",
"type": 12,
"value": "GTS"
}, {
"name": "since",
"type": 12,
"value": "2014"
}]
}],
"tableName": "product"
},
"beforeImage": {
"rows": [{
"fields": [{
"name": "id",
"type": 4,
"value": 1
}, {
"name": "name",
"type": 12,
"value": "TXC"
}, {
"name": "since",
"type": 12,
"value": "2014"
}]
}],
"tableName": "product"
},
"sqlType": "UPDATE"
}],
"xid": "xid:xxx"
}
九、AT模式详解 [Auto Trans]
AT模式运行机制
? AT模式是整个Seata分布式事务处理中最简单 一种操作模型、可以这么说、几乎不需要修改任何程序的代码,就可以直接实现分布式事务的处理。AT模式是基于XA事务演化而来的一个分布式事务处理模型、最大的特点是需要在每个微服务的数据库之中额外创建 undo_log 数据表。
? AT模式的特点就是对业务无入侵式,整体机制分为二阶段提交。用户只需要关注自己的业务SQL,用户的业务SQL作为一阶段,Seata框架会自动生成的事务的二阶段提交和回滚操作。
两阶段提交协议的演变:
- 一阶段:业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。
- 二阶段:
- 提交异步化,非常快速地完成。
- 回滚通过一阶段的回滚日志进行反向补偿。
就是以前的2PC是整个事务过程中,只要不提交或回滚,就持续占有锁资源,而seata是异步方式,直接本地执行或回滚,本地事务提交前,先拿到该记录的 全局锁,本地提交释放本地锁,完全不需要去等其他业务一起commit,因为有Seata来管理这些,他会从log中判断是否回滚,如果回滚,根据日志执行事务补偿。
Seata具体实现步骤
1、TM端使用 @GlobalTransaction 开启全局事务、提交、回滚。
2、TM开始RPC远程调用RM节点
3、RM端seata-client 通过扩展DataSourceProxy ,实现自动生成UNDO_LOG 与TC 上报
4、TM告知TC提交/回滚全局事务
5、TC通知RM执行commit / rollback 指令。 同时清除 undo_log 表
SeataDemo:
流程梳理:首先需要下载好Seata. server 【这里演示1.3.】 后台启动Nacos、更改Seata配置文件。 启动查看Nacos服务。启动Seata服务、出现端口8091则启动成功。 要求在每个服务数据库都要创建undo_log表【回滚日志】。 犹豫1.1.0之后Seata官方不在提供 初始化语句。故我在下面贴出。
CREATE TABLE `undo_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`branch_id` bigint(20) NOT NULL,
`xid` varchar(100) NOT NULL,
`context` varchar(128) NOT NULL,
`rollback_info` longblob NOT NULL,
`log_status` int(11) NOT NULL,
`log_created` datetime NOT NULL,
`log_modified` datetime NOT NULL,
`ext` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
文件修改
MySql事务日志持久化
下面指定存储类型为数据库: 注意此处一定要初始化,并且1.0之后的版本没有sql脚本,这里我贴出来。 三张表在下面做详细解释。
-- the table to store GlobalSession data
drop table if exists `global_table`;
create table `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`)
);
-- the table to store BranchSession data
drop table if exists `branch_table`;
create table `branch_table` (
`branch_id` bigint not null,
`xid` varchar(128) not null,
`transaction_id` bigint ,
`resource_group_id` varchar(32),
`resource_id` varchar(256) ,
`lock_key` varchar(128) ,
`branch_type` varchar(8) ,
`status` tinyint,
`client_id` varchar(64),
`application_data` varchar(2000),
`gmt_create` datetime,
`gmt_modified` datetime,
primary key (`branch_id`),
key `idx_xid` (`xid`)
);
-- the table to store lock data
drop table if exists `lock_table`;
create table `lock_table` (
`row_key` varchar(128) not null,
`xid` varchar(96),
`transaction_id` long ,
`branch_id` long,
`resource_id` varchar(256) ,
`table_name` varchar(32) ,
`pk` varchar(36) ,
`gmt_create` datetime ,
`gmt_modified` datetime,
primary key(`row_key`)
);
使用Nacos作为配置中心、能对对配置做优化处理。
步骤如下:
? 1、在Nacos中,添加命名空间seata,获取到namespace 的id
? 2、修改registry.conf 文件中的registry 部分type = “nacos”,同时修改nacos 部分的配置
? 3、修改registry.conf 文件中的config 部分type = “nacos”,同时修改nacos 部分的配置
? 4、下载nacos-config.脚本 和config.txt 下载地址:Seata脚本与配置 内容如下:
-
nacos-config.sh和nacos-config.py选择一个:在seata目录下新建 script 目录,将 nacos-config.sh 放入script 目录下 -
config.txt :该文件存放在将seata目录下,与conf、lib 目录同级,seata的非常全的配置内容,可通过nacos-config.sh 脚本推送到nacos 配置中心 -
修改config.txt 的内容 -
打开git bash 或linux 类命令行,执行sh脚本(注意脚本是否有执行的权限)
这里自动设置logs生成位置。所以需要在config新建这个文件夹、并指定位置。
一些关键子模块依赖
<!--子模块order-service和storage-service的pom中添加nacos和seata依赖-->
<dependencies>
<!--nacos注册中心和配置中心-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!--seata-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
<exclusions>
<!--移除掉该starter中自带的依赖,该依赖版本较低-->
<exclusion>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--单独添加seata 1.3.0的依赖-->
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>1.3.0</version>
</dependency>
<!--openfeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
<version>10.2.3</version>
</dependency>
<!--Mybatis通用Mapper-->
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper</artifactId>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
AT模式原理解析
一阶段步骤:
TM方法执行时、由于该方法具有 @GlobalTranscational`标志,该TM会向TC发起全局事务,生成XID(全局锁)
RM会通过写表、UNDO_LOG记录回滚日志(Branch ID),通知TC操作结果
RM写表的过程,Seata 会拦截业务SQL,首先解析 SQL 语义,在业务数据被更新前,将其保存成before image,然后执
行业务SQL,在业务数据更新之后,再将其保存成after image,最后生成行锁。以上操作全部在一个数据库事务内完
成,这样保证了一阶段操作的原子性。
二阶段步骤:
因为“业务 SQL”在一阶段已经提交至数据库, 所以 Seata 框架只需将一阶段保存的快照数据和行锁删掉,完成数据清理即可。
- 正常:TM执行成功,通知TC全局提交,TC此时通知所有的RM提交成功,删除UNDO_LOG回滚日志
-
异常:TM执行失败,通知TC全局回滚,TC此时通知所有的RM进行回滚,根据UNDO_LOG反向操作,使用before image还原业务数据,删除UNDO_LOG,但在还原前要首先要校验脏写,对比“数据库当前业务数据”和 “after image”,如果两份数据完全一致就说明没有脏写,可以还原业务数据,如果不一致就说明有脏写,出现脏写就需要转 人工处理。
AT模式读写隔离 [ 锁机制 ]
Seata.io
十、XA模式
? Seata之中除了存在AT模式外、还提供一种基于数据库的XA模式、但是XA模式是需要数据库支持、这种时候如果使用、因为是基于Durid操作。 那可以使用Seata提供的DataSourceProxyXA工具类进行包装
@Configuration
public class XADataSourceConfiguration {
@Bean("dataSourceProxy")
public DataSource dataSource(DruidDataSource druidDataSource){
return new DataSourceProxyXA(druidDataSource);
}
}
? XA与AT简单对比
? 两者之间都属于简单的分布式事务实现。但XA是基于数据的操作实现、而AT是纯粹的通过程序逻辑来实现的。
十一、TCC模式详解
TCC 模式需要用户根据自己的业务场景实现 Try、Confirm 和 Cancel 三个操作;事务发起方在一阶段执行 Try 方式,在二阶段提交执行 Confirm 方法,二阶段回滚执行 Cancel 方法。
TCC 三个方法描述:
- Try:资源的检测和预留;
- Confirm:执行的业务操作提交;要求 Try 成功 Confirm 一定要能成功;
- Cancel:预留资源释放;
TCC 的实践经验
蚂蚁金服TCC实践,总结以下注意事项: ? 业务模型分2阶段设计 ? 并发控制 ? 允许空回滚 ? 防悬挂控制 ? 幂等控制
1 TCC 设计 - 业务模型分 2 阶段设计
用户接入 TCC ,最重要的是考虑如何将自己的业务模型拆成两阶段来实现。
以“扣钱”场景为例,在接入 TCC 前,对 A 账户的扣钱,只需一条更新账户余额的 SQL 便能完成;但是在接入 TCC 之后,用户就需要考虑如何将原来一步就能完成的扣钱操作,拆成两阶段,实现成三个方法,并且保证一阶段 Try 成功的话 二阶段 Confirm 一定能成功。
如上图所示,Try 方法作为一阶段准备方法,需要做资源的检查和预留。在扣钱场景下,Try 要做的事情是就是检查账户余额是否充足,预留转账资金,预留的方式就是冻结 A 账户的 转账资金。Try 方法执行之后,账号 A 余额虽然还是 100,但是其中 30 元已经被冻结了,不能被其他事务使用。
二阶段 Confirm 方法执行真正的扣钱操作。Confirm 会使用 Try 阶段冻结的资金,执行账号扣款。Confirm 方法执行之后,账号 A 在一阶段中冻结的 30 元已经被扣除,账号 A 余额变成 70 元 。
如果二阶段是回滚的话,就需要在 Cancel 方法内释放一阶段 Try 冻结的 30 元,使账号 A 的回到初始状态,100 元全部可用。
用户接入 TCC 模式,最重要的事情就是考虑如何将业务模型拆成 2 阶段,实现成 TCC 的 3 个方法,并且保证 Try 成功 Confirm 一定能成功。相对于 AT 模式,TCC 模式对业务代码有一定的侵入性,但是 TCC 模式无 AT 模式的全局行锁,TCC 性能会比 AT 模式高很多。
2 TCC 设计 - 允许空回滚
Cancel 接口设计时需要允许空回滚。在 Try 接口因为丢包时没有收到,事务管理器会触发回滚,这时会触发 Cancel 接口,这时 Cancel 执行时发现没有对应的事务 xid 或主键时,需要返回回滚成功。让事务服务管理器认为已回滚,否则会不断重试,而 Cancel 又没有对应的业务数据可以进行回滚。
3 TCC 设计 - 防悬挂控制
悬挂的意思是:Cancel 比 Try 接口先执行,出现的原因是 Try 由于网络拥堵而超时,事务管理器生成回滚,触发 Cancel 接口,而最终又收到了 Try 接口调用,但是 Cancel 比 Try 先到。按照前面允许空回滚的逻辑,回滚会返回成功,事务管理器认为事务已回滚成功,则此时的 Try 接口不应该执行,否则会产生数据不一致,所以我们在 Cancel 空回滚返回成功之前先记录该条事务 xid 或业务主键,标识这条记录已经回滚过,Try 接口先检查这条事务xid或业务主键如果已经标记为回滚成功过,则不执行 Try 的业务操作。
4 TCC 设计 - 幂等控制
幂等性的意思是:对同一个系统,使用同样的条件,一次请求和重复的多次请求对系统资源的影响是一致的。因为网络抖动或拥堵可能会超时,事务管理器会对资源进行重试操作,所以很可能一个业务操作会被重复调用,为了不因为重复调用而多次占用资源,需要对服务设计时进行幂等控制,通常我们可以用事务 xid 或业务主键判重来控制。
十二、SAGA模式详解
Saga 是一种补偿协议,在 Saga 模式下,分布式事务内有多个参与者,每一个参与者都是一个冲正补偿服务,需要用户根据业务场景实现其正向操作和逆向回滚操作。
【Saga模式是SEATA提供的长事务解决方案,在Saga模式中,业务流程中每个参与者都提交本地事务,当出现某一个参与者失败则补偿前面已经成功的参与者,一阶段正向服务和二阶段补偿服务都由业务开发实现。】
特殊环境下、如果要与一些封闭的系统、或者是老旧系统进行分布式业务对接、那么AT、TCC模型将失效。而为了解决这样的技术问题,在Seata中引入Saga模型
Saga调用模式
目前Seata提供的Saga模式是基于状态机引擎实现的、需要开发者收购的进行Saga业务员流程的绘制、并将其转换为Json配置文件、而后在程序运行时、将一句此配置文件实现业务处理以及服务补偿处理。而要将进行Saga状态图的绘制。一般通过Saga状态机来实现。
状态机展示 Saga
?
如图:T1T3都是正向的业务流程,都对应着一个冲正逆向操作C1C3
分布式事务执行过程中,依次执行各参与者的正向操作,如果所有正向操作均执行成功,那么分布式事务提交。如果任何一个正向操作执行失败,那么分布式事务会退回去执行前面各参与者的逆向回滚操作,回滚已提交的参与者,使分布式事务回到初始状态。
Saga 正向服务与补偿服务也需要业务开发者实现。因此是业务入侵的。
Saga 模式下分布式事务通常是由事件驱动的,各个参与者之间是异步执行的,Saga 模式是一种长事务解决方案。
Saga 模式使用场景
Saga 模式适用于业务流程长且需要保证事务最终一致性的业务系统,Saga 模式一阶段就会提交本地事务,无锁、长流程情况下可以保证性能。
事务参与者可能是其它公司的服务或者是遗留系统的服务,无法进行改造和提供 TCC 要求的接口,可以使用 Saga 模式。
Saga模式的优势与缺点
优势
- 一阶段提交本地数据库事务,无锁,高性能;
- 参与者可以采用事务驱动异步执行,高吞吐
- 补偿服务即正向服务的“反向”,易于理解,易于实现;
缺点
Saga 模式由于一阶段已经提交本地数据库事务,且没有进行“预留”动作,所以不能保证隔离性。
注意
与TCC实践经验相同的是,Saga 模式中,每个事务参与者的冲正、逆向操作,需要支持:
- 空补偿:逆向操作早于正向操作时;
- 防悬挂控制:空补偿后要拒绝正向操作
- 幂等性
十三、AT、TCC、SAGA模式分析
- AT 模式是无侵入的分布式事务解决方案,适用于不希望对业务进行改造的场景。优点在于:简单使用,分布式事务最简单的处理方法。 缺点:存在全局锁这一概念。可能会出现严重的性能问题 【待总结AT出现问题的场景】。
- TCC 模式是高性能分布式事务解决方案,适用于核心系统等对性能有很高要求的场景。优点:没有全局锁、性能较AT好,但是代码侵入十分严重。复杂度提升。
- Saga 模式是长事务解决方案,适用于业务流程长且需要保证事务最终一致性的业务系统,Saga 模式一阶段就会提交本地事务,无锁,长流程情况下可以保证性能,多用于渠道层、集成层业务系统。事务参与者可能是其它公司的服务或者是遗留系统的服务,无法进行改造和提供 TCC 要求的接口,也可以使用 Saga 模式。
参考文章视频等:君哥Seata笔记、 Seata官网、YOOTK沐言优拓 【李兴华老师】。 Seata官方学习群:216012363
|