实用指南:SpringCloud Alibaba-Seata处理分布式事务
分布式事务
- 一次业务操作需要跨多个数据源或需要跨多个系统进行远程调用,就会产生分布式事务问题
- 例如:三个模块被拆分成三个独立的应用,分别使用三个独立的数据源,业务操作需要调用三个服务来完成。此时每个服务内部的数据一致性由本地事务来保证,但是全局的数据一致性问题没法保证

Seata介绍
- Simple Extensible Autonomous Transaction Architecture,简单可扩展自治事务框架
- 开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务
- 官网地址:Seata
- GitHub:https://github.com/seata/seata
工作流程说明
核心逻辑即全局事务ID的传递和变更
XID
全局事务的唯一ID,可以在服务的调用链路中传递,绑定到服务的事务上下文中
TC->TM->RM
- Transaction Coordinator (TC):事务协调器,维护全局事务的运行状态,负责协调并驱动全局事务的提交或回滚
- Transaction Manager (TM):事务管理器,标注注解@GlobleTransactional启动入口的微服务模块,是全局事务的发起者,负责定义全局事务的范围,并根据TC维护的全局事务和分支事务状态,最终发起全局提交或全局回滚的决议
- Resource Manager (RM):资源管理器,即代表数据库,负责管理分支事务上的资源,向TC注册分支事务、汇报分支事务状态,并接收TC事务协调器的指令,驱动分支(本地)事务的提交和回滚
- 三个组件相互协作,TC以Seata 服务器(Server)形式独立部署,TM和RM则是以Seata Client的形式集成在微服务中运行

处理流程
- TM 向 TC 申请开启一个全局事务,全局事务创建成功并生成一个全局唯一的 XID
- XID 在微服务调用链路的上下文中传播
- RM 向 TC 注册分支事务,将其纳入 XID 对应全局事务的管辖
- TM 向 TC 发起针对 XID 的全局提交或回滚决议
- TC 调度 XID 下管辖的全部分支事务完成提交或回滚请求

下载安装
- 下载地址:https://github.com/seata/seata/tags
- 修改配置文件参数配置,参考官网

- 建立seata的数据库,相关建表语句在安装包中

- 执行bin目录下的seata-server.bat启动seata
- 访问seata,账号密码均是seata,http://localhost:7091

- 访问nacos

案例实现
- POM依赖引入
com.alibaba.cloud
spring-cloud-starter-alibaba-seata
YML配置
# ========================seata===================
seata:
registry:
type: nacos
nacos:
server-addr: 127.0.0.1:8848
namespace: ""
group: SEATA_GROUP
application: seata-server
tx-service-group: default_tx_group # 事务组,由它获得TC服务的集群名称
service:
vgroup-mapping: # 点击源码分析
default_tx_group: default # 事务组与TC服务集群的映射关系
data-source-proxy-mode: AT
- 在service逻辑方法上标注注解@GlobalTransactional
//Exception异常时进行回滚,name需要唯一
@GlobalTransactional(name = "fsp-create-order",rollbackFor = Exception.class)
public void create(Order order) {
log.info("------->下单开始");
orderDao.create(order);
//远程调用库存服务扣减库存
log.info("------->order-service中扣减库存开始");
storageService.decrease(order.getProductId(), order.getCount());
log.info("------->order-service中扣减库存结束");
//远程调用账户服务扣减余额
log.info("------->order-service中扣减余额开始");
accountService.decrease(order.getUserId(),order.getMoney());
log.info("------->order-service中扣减余额结束");
//修改订单状态为已完成
log.info("------->order-service中修改订单状态开始");
orderDao.update(order.getUserId(),0);
log.info("------->order-service中修改订单状态结束");
log.info("------->下单结束");
}
AT模式
整体机制
- 一阶段:业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源
- 二阶段:提交异步化,非常快速地完成。回滚通过一阶段的回滚日志进行反向补偿
一阶段加载
- 在一阶段,Seata 会拦截“业务 SQL”
- 解析 SQL 语义,找到“业务 SQL”要更新的业务数据,在业务数据被更新前,将其保存成“before image”
- 执行“业务 SQL”更新业务数据,在业务数据更新之后,将其保存成“after image”,最后生成行锁
- 以上操作全部在一个数据库事务内完成,这样保证了一阶段操作的原子性

二阶段提交
- 二阶段,如果业务逻辑顺利执行完成,将SQL数据提交
- 因为“业务 SQL”在一阶段已经提交至数据库,所以Seata框架只需将一阶段保存的快照数据和行锁删掉,完成数据清理即可

二阶段回滚
- 二阶段如果是回滚的话,Seata 就需要回滚一阶段已经执行的“业务 SQL”,还原业务数据
- 回滚方式便是用“before image”还原业务数据;但在还原前要首先要校验脏写,对比“数据库当前业务数据”和 “after image”
- 如果两份数据完全一致就说明没有脏写,可以还原业务数据,如果不一致就说明有脏写,出现脏写就需要转人工处理

浙公网安备 33010602011771号