目录
一、Seata
1、Seata-AT模式
1.1、具体案例
1.2、通过Seata的AT模式解决分布式事务
2、Seata-XA模式
3、Seata-TCC模式
4、Seata-SAGA模式
一、Seata
1、Seata-AT模式
概念:AT模式是一种无侵入的分布式事务解决方案,在 AT 模式下,用户只需关注自己的“业务 SQL”,用户的 “业务 SQL” 作为一阶段,Seata 框架会自动生成事务的二阶段提交和回滚操作。
Seata AT 模式
两阶段提交协议的演变:
一阶段:业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。
二阶段:提交异步化,非常快速地完成。回滚通过一阶段的回滚日志进行反向补偿。
在一阶段中,Seata会拦截“业务SQL“,首先解析SQL语义,找到要更新的业务数据,在数据被更新前,保存下来"undo",然后执行”业务SQL“更新数据,更新之后再次保存数据”redo“,最后生成行锁,这些操作都在本地数据库事务内完成,这样保证了一阶段的原子性。
二阶段
相对一阶段,二阶段比较简单,负责整体的回滚和提交,如果之前的一阶段中有本地事务没有通过,那么就执行全局回滚,否在执行全局提交,回滚用到的就是一阶段记录的"undo Log",通过回滚记录生成反向更新SQL并执行,以完成分支的回滚。当然事务完成后会释放所有资源和删除所有日志。
1.1、具体案例
1、创建两个服务,一个订单order8801 一个库存stock8802
创建数据库表
-- stock库存表
DROP TABLE IF EXISTS `t_stock`;
CREATE TABLE `t_stock` (
`product_id` int(11) NOT NULL AUTO_INCREMENT,
`money` int(11) DEFAULT 0,
`count` int(11) DEFAULT 0,
PRIMARY KEY (`product_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into t_stock (product_id,count,money) values (1,100,10);
-- order订单表
DROP TABLE IF EXISTS `t_order`;
CREATE TABLE `t_order` (
`product_id` int(11) NOT NULL AUTO_INCREMENT,
`count` int(11) DEFAULT 0,
PRIMARY KEY (`product_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
业务:订单服务通过OpenFegin远程调用库存服务,然后库存服务减库存,订单服务生成订单,完成基本的调用以后我们给订单服务添加异常
pom
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<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-seata</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
</dependencies>
yml
server:
port: 8801
spring:
application:
name: seata-order
cloud:
nacos:
discovery:
server-addr: localhost:8848
namespace: "b98dfc17-7de8-44f1-b1e1-837fe681b96c"
group: SEATA_GROUP
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/seata_test?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
username: root
password: root
seata:
tx-service-group: mygroup
service:
vgroup-mapping:
mygroup: default
registry:
type: nacos
nacos:
server-addr: localhost:8848
group: "SEATA_GROUP"
namespace: "b98dfc17-7de8-44f1-b1e1-837fe681b96c"
username: "nacos"
password: "nacos"
补充对应关系
stock8802如下端口和服务名不同其他同上
server:
port: 8802
spring:
application:
name: seata-stock
订单order8801
启动类
@EnableDiscoveryClient
@EnableFeignClients
@MapperScan("com.lwz.springcloud.mapper")
@SpringBootApplication
public class Order8801Main {
public static void main(String[] args) {
SpringApplication.run(Order8801Main.class,args);
}
}
控制层
@RestController
public class OrderController {
@Autowired
private OrderService orderService;
@GetMapping("/order/create")
public String create(){
orderService.create();
return "生成订单";
}
}
service
public interface OrderService {
void create();
}
serviceimpl
@Service
public class OrderServiceImpl implements OrderService {
@Resource
private OrderMapper orderMapper;
@Resource
private StockService stockService;
@Override
public void create() {
// 减库存
stockService.decrement();
// 添加异常
int i = 1/0;
// 创建订单
orderMapper.create();
}
}
mapper
public interface OrderMapper {
@Insert("insert into t_order (count) values (1)")
void create();
}
feign
@FeignClient(value = "seata-stock")
public interface StockService {
@GetMapping("/stock/decr")
String decrement();
}
库存stock8802
启动类
@SpringBootApplication
@MapperScan("com.lwz.springcloud.mapper")
@EnableDiscoveryClient
public class Stock8802Main {
public static void main(String[] args) {
SpringApplication.run(Stock8802Main.class,args);
}
}
控制层
@RestController
public class StockController {
@Resource
private StockService stockService;
@GetMapping("/stock/decr")
public String decrement(){
stockService.decr();
return "库存-1";
}
}
service
public interface StockService {
void decr();
}
serviceimpl
@Service
public class StockServiceImpl implements StockService {
@Resource
private StockMapper stockMapper;
@Override
public void decr() {
stockMapper.decr();
}
}
mapper
public interface StockMapper {
@Update("update t_stock set count = count - 1 where product_id = 1")
void decr();
}
Nacos和Seata都要进行启动,8801和8802起来
访问Order的接口测试:http://localhost:8801/order/create
此时我们会发现访问接口出现异常情况,但是库存减少,订单没有增加此时已经出现了分布式事务的问题
库存数量减一,但是订单表没数据
1.2、通过Seata的AT模式解决分布式事务
Seata依赖,上面已经添加过
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>
在对应的微服务数据库上加上undo_log表,此表用于数据的回滚
undo_log表去官网上找,或源代码seata/script/client/at/db/mysql.sql下面找到AT需要的undo_log表
-- 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 = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8mb4 COMMENT ='AT transaction mode undo table';
ALTER TABLE `undo_log` ADD INDEX `ix_log_created` (`log_created`);
需要在order8801(TM)的Controller上添加注解
@RestController
public class OrderController {
@Autowired
private OrderService orderService;
@GetMapping("/order/create")
@GlobalTransactional// 开启分布式事务
public String create(){
orderService.create();
return "生成订单";
}
}
把t_stock表删除,再把最上面的SQL语句重新执行一次,重启服务进行测试。
Nacos和Seata都要进行启动,8801和8802起来
访问Order的接口测试:http://localhost:8801/order/create
访问接口仍然会报异常,但是此时已经解决了分布式事务问题。
库存没有减少,订单也没有增加
那么为了验证undo_log表用于存储回滚的数据,我们在OrderServiceImpl上异常位置添加断点,同时以debug方式来启动8801订单服务。
然后访问接口:http://localhost:8801/order/create,程序会卡在断点上,此时我们来查看undo_log表和库存表,此时我们会发现,库存确实减少了,但是在undo_log表中出现了快照记录了当前修改前的数据,这个数据就是用于回滚的数据
undo_log表记录快照
库存减少
放行以后,库存数量回复,回滚生效
2、Seata-XA模式
Seata XA
什么是XA?
XA 规范早在上世纪 90 年代初就被提出,用以解决分布式事务处理这个领域的问题。
注意:不存在某一种分布式事务机制可以完美适应所有场景,满足所有需求。
现在,无论 AT 模式、TCC 模式还是 Saga 模式,这些模式的提出,本质上都源自 XA 规范对某些场景需求的无法满足。
什么是XA协议?
XA 规范 是 X/Open 组织定义的分布式事务处理(DTP,Distributed Transaction Processing)标准
XA 规范 描述了全局的事务管理器与局部的资源管理器之间的接口。 XA规范 的目的是允许的多个资源(如数据库,应用服务器,消息队列等)在同一事务中访问,这样可以使 ACID 属性跨越应用程序而保持有效。
XA 规范 使用两阶段提交(2PC,Two-Phase Commit)来保证所有资源同时提交或回滚任何特定的事务。
XA 规范 在上世纪 90 年代初就被提出。目前,几乎所有主流的数据库都对 XA 规范 提供了支持。
DTP模型定义如下角色:
- AP:即应用程序,可以理解为使用DTP分布式事务的程序
- RM:资源管理器,可以理解为事务的参与者,一般情况下是指一个数据库的实例(MySql),通过资源管理器对该数据库进行控制,资源管理器控制着分支事务
- TM:事务管理器,负责协调和管理事务,事务管理器控制着全局事务,管理实务生命周期,并协调各个RM。全局事务是指分布式事务处理环境中,需要操作多个数据库共同完成一个工作,这个工作即是一个全局事务。
- DTP模式定义TM和RM之间通讯的接口规范叫XA,简单理解为数据库提供的2PC接口协议,基于数据库的XA协议来实现的2PC又称为XA方案。
3、Seata-TCC模式
TCC 是分布式事务中的二阶段提交协议,它的全称为 Try-Confirm-Cancel,即资源预留(Try)、确认操作(Confirm)、取消操作(Cancel),他们的具体含义如下:
1. Try:对业务资源的检查并预留;
2. Confirm:对业务处理进行提交,即 commit 操作,只要 Try 成功,那么该步骤一定成功;
3. Cancel:对业务处理进行取消,即回滚操作,该步骤回对 Try 预留的资源进行释放。
TCC 是一种侵入式的分布式事务解决方案,以上三个操作都需要业务系统自行实现,对业务系统有着非常大的入侵性,设计相对复杂,但优点是 TCC 完全不依赖数据库,能够实现跨数据库、跨应用资源管理,对这些不同数据访问通过侵入式的编码方式实现一个原子操作,更好地解决了在各种复杂业务场景下的分布式事务问题。
TCC和AT区别
AT 模式基于支持本地ACID事务的关系型数据库:
一阶段 prepare 行为:在本地事务中,一并提交业务数据更新和相应回滚日志记录。
二阶段 commit 行为:马上成功结束,自动异步批量清理回滚日志。
二阶段 rollback 行为:通过回滚日志,自动 生成补偿操作,完成数据回滚。
相应的,TCC 模式,不依赖于底层数据资源的事务支持:
一阶段 prepare 行为:调用 自定义的 prepare 逻辑。
二阶段 commit 行为:调用 自定义的 commit 逻辑。
二阶段 rollback 行为:调用 自定义的 rollback 逻辑。
所谓 TCC 模式,是指支持把 自定义的分支事务纳入到全局事务的管理中。
特点:
1. 侵入性比较强,并且需要自己实现相关事务控制逻辑
2. 在整个过程基本没有锁,性能较强
TCC具体案例
4、Seata-SAGA模式
Saga模式是SEATA提供的长事务解决方案,在Saga模式中,业务流程中每个参与者都提交本地事务,当出现某一个参与者失败则补偿前面已经成功的参与者,一阶段正向服务和二阶段补偿服务(执行处理时候出错了,给一个修复的机会)都由业务开发实现。
Seata saga模式
Saga 模式下分布式事务通常是由事件驱动的,各个参与者之间是异步执行的,Saga 模式是一种长事务解决方案。
Spring Cloud Alibaba Seata(一)
怕输的人永远没有资格赢!