1:seata概念
Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。
1:seata中的角色
TC (Transaction Coordinator) - 事务协调者
维护全局和分支事务的状态,驱动全局事务提交或回滚。
TM (Transaction Manager) - 事务管理器
定义全局事务的范围:开始全局事务、提交或回滚全局事务。他也是一个RM
RM (Resource Manager) - 资源管理器
管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。
2:seata中at模型说明
1:seata整体机制
整体机制
两阶段提交协议的演变:
一阶段:业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。
二阶段:
- 提交异步化,非常快速地完成。
- 回滚通过一阶段的回滚日志进行反向补偿。
总的来说就是:
- 服务A调用服务B,那么此时出现3个锁,A的本地锁,B的本地锁,整条链路的全局锁
- A提交,此时A获得本地锁,执行,释放本地锁A,然后向TC报告,TC手里有全局锁
- 当服务B出现异常时,全局锁根据A报告的记录,进行全局锁的回滚
2:seata的写隔离
一阶段本地事务提交前,需要确保先拿到 全局锁 。
拿不到 全局锁 ,不能提交本地事务。
拿 全局锁 的尝试被限制在一定范围内,超出范围将放弃,并回滚本地事务,释放本地锁。
3:seata的多事务情况下回滚
1:当分布式1中的服务A已经提交,并且释放了本地锁,分布式2中的服务A获得了本地锁,执行完成等待全局锁
2:分布式1中的服务B爆出了异常,那么分布式1中的全局锁控制分布式1中的服务A的本地锁回滚
3:但是服务A的本地锁被分布式2中的A占用,那么分布式1的全局锁等待分布式2中的抢占全局锁失败后,再拿到A的本地锁回滚
3:seata的tcc模式
一个分布式的全局事务,整体是 两阶段提交 的模型。全局事务是由若干分支事务组成的,分支事务要满足 两阶段提交 的模型要求,即需要每个分支事务都具备自己的:
一阶段 prepare 行为
二阶段 commit 或 rollback 行为
相比较AT 模式(参考链接 TBD)基于 支持本地 ACID 事务 的 关系型数据库:
- 一阶段 prepare 行为:在本地事务中,一并提交业务数据更新和相应回滚日志记录。
- 二阶段 commit 行为:马上成功结束,自动 异步批量清理回滚日志。
- 二阶段 rollback 行为:通过回滚日志,自动 生成补偿操作,完成数据回滚。
相应的,TCC 模式,不依赖于底层数据资源的事务支持:
- 一阶段 prepare 行为:调用 自定义 的 prepare 逻辑。
- 二阶段 commit 行为:调用 自定义 的 commit 逻辑。
- 二阶段 rollback 行为:调用 自定义 的 rollback 逻辑。
所谓 TCC 模式,是指支持把 自定义 的分支事务纳入到全局事务的管理中。
2:seata的AT模式
1:下载seata-server
http://seata.io/zh-cn/blog/download.html
2:修改seata-server配置
1:seata\conf\registry.conf
2:seata\conf\seata\conf
3:创建setat-server-db用来保存seata事务信息
自己建库,自己建表。
数据库名和 file.conf中一致。
分支事务表
CREATE TABLE `branch_table` (
`branch_id` bigint(20) NOT NULL,
`xid` varchar(128) NOT NULL,
`transaction_id` bigint(20) DEFAULT NULL,
`resource_group_id` varchar(32) DEFAULT NULL,
`resource_id` varchar(256) DEFAULT NULL,
`branch_type` varchar(8) DEFAULT NULL,
`status` tinyint(4) DEFAULT NULL,
`client_id` varchar(64) DEFAULT NULL,
`application_data` varchar(2000) DEFAULT NULL,
`gmt_create` datetime(6) DEFAULT NULL,
`gmt_modified` datetime(6) DEFAULT NULL,
PRIMARY KEY (`branch_id`),
KEY `idx_xid` (`xid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
全局事务表
CREATE TABLE `global_table` (
`xid` varchar(128) NOT NULL,
`transaction_id` bigint(20) DEFAULT NULL,
`status` tinyint(4) NOT NULL,
`application_id` varchar(32) DEFAULT NULL,
`transaction_service_group` varchar(32) DEFAULT NULL,
`transaction_name` varchar(128) DEFAULT NULL,
`timeout` int(11) DEFAULT NULL,
`begin_time` bigint(20) DEFAULT NULL,
`application_data` varchar(2000) DEFAULT NULL,
`gmt_create` datetime DEFAULT NULL,
`gmt_modified` datetime DEFAULT NULL,
PRIMARY KEY (`xid`),
KEY `idx_gmt_modified_status` (`gmt_modified`,`status`),
KEY `idx_transaction_id` (`transaction_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
全局锁
CREATE TABLE `lock_table` (
`row_key` varchar(128) NOT NULL,
`xid` varchar(96) DEFAULT NULL,
`transaction_id` bigint(20) DEFAULT NULL,
`branch_id` bigint(20) NOT NULL,
`resource_id` varchar(256) DEFAULT NULL,
`table_name` varchar(32) DEFAULT NULL,
`pk` varchar(36) DEFAULT NULL,
`gmt_create` datetime DEFAULT NULL,
`gmt_modified` datetime DEFAULT NULL,
PRIMARY KEY (`row_key`),
KEY `idx_branch_id` (`branch_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
4:启动seata
win:seata\bin\seata-server.bat
5:配置TM
1:pom
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-seata</artifactId>
<version>2.2.0.RELEASE</version>
</dependency>
2:配置
下面两个配置,不配置也行,走默认8091。
- registry.conf
- file.conf
然后application.yml中就可以什么都不用配置了
3:注解
我们只需要使用一个 @GlobalTransactional 注解在业务方法上:
@GetMapping("/one")
@GlobalTransactional(rollbackFor = Exception.class)
public String one() throws InterruptedException {
rmOneService.rm1();
// TimeUnit.MINUTES.sleep(1);
System.out.println(1/0);
return "success";
}
4:在每一个客户端生成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=45 DEFAULT CHARSET=utf8;
6:配置RM
1:pom
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-seata</artifactId>
<version>2.2.0.RELEASE</version>
</dependency>
2:配置
下面两个配置,不配置也行,走默认8091。
- registry.conf
- file.conf
然后application.yml中就可以什么都不用配置了
3:注解
不用再配置了,正常写就行
4:在每一个客户端生成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=45 DEFAULT CHARSET=utf8;
3:seata的TCC模式
1-4和AT模式一样,都是修改启动seata-server
5:配置TM
1:pom
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-seata</artifactId>
<version>2.2.0.RELEASE</version>
</dependency>
2:配置
下面两个配置,不配置也行,走默认8091。
- registry.conf
- file.conf
然后application.yml中就可以什么都不用配置了
3:注解
我们只需要使用一个 @GlobalTransactional 注解在业务方法上:
@GetMapping("/one")
@GlobalTransactional(rollbackFor = Exception.class)
public String one() throws InterruptedException {
rmOneService.rm1();
// TimeUnit.MINUTES.sleep(1);
System.out.println(1/0);
return "success";
}
4:在tm的方法调用的其他方法上加@LocalTCC
tm的入口方法
@GetMapping("/one-tcc")
@GlobalTransactional(rollbackFor = Exception.class)
public String oneTcc() throws InterruptedException {
rmOneInterface.rm1(null);
return "success";
}
tm调用的方法接口
package com.example.one.service;
import io.seata.rm.tcc.api.BusinessActionContext;
import io.seata.rm.tcc.api.LocalTCC;
import io.seata.rm.tcc.api.TwoPhaseBusinessAction;
@LocalTCC
public interface RmOneInterface {
@TwoPhaseBusinessAction(name = "rm1TccAction" , commitMethod = "rm1Commit" ,rollbackMethod = "rm1Rollback")
public String rm1(BusinessActionContext businessActionContext);
public boolean rm1Commit(BusinessActionContext businessActionContext);
public boolean rm1Rollback(BusinessActionContext businessActionContext);
}
tm调用方法接口的实现类,为当前自己的方法加上本地事务
@Override
@Transactional
public String rm1(BusinessActionContext businessActionContext) {
// 查询是事务记录表,xxxx
System.out.println("rm1 try");
rm2();
rm3();
System.out.println(1/0);
return null;
}
6:配置RM
1:pom
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-seata</artifactId>
<version>2.2.0.RELEASE</version>
</dependency>
2:配置
下面两个配置,不配置也行,走默认8091。
- registry.conf
- file.conf
然后application.yml中就可以什么都不用配置了
3:注解
不用再配置了,正常写就行
4:不需要再生成undo_log表
try,confirm,cancel 中独立事务。通过业务回滚。此时不用undo.log表了。