1.LCN
2ByteTCC
3.Seata
Seata 是阿里开源的分布式事务框架,属于二阶段提交模式。
核心组件
- 事务协调器 TC:维护全局和分支事务的状态,指示全局提交或者回滚。
- 事务管理者 TM:开启、提交或者回滚一个全局事务。
- 资源管理者 RM:管理执行分支事务的那些资源,向TC注册分支事务、上报分支事务状态、控制分支事务的提交或者回滚。
模式
AT(Auto Transaction)
AT模式是Seata默认的工作模式。需要基于支持本地 ACID 事务的关系型数据库,Java 应用,通过 JDBC 访问数据库。
- 一阶段:业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。
- 二阶段:提交异步化,非常快速地完成;回滚通过一阶段的回滚日志进行反向补偿。
步骤1开启全局事务;步骤2注册分支事务,这里对应着一阶段;步骤3提交或者回滚分支事务,对应着二阶段。

TCC
该模式工作分为三个阶段:prepare/commit/cancel。
- TM向TC申请全局事务XID,传播给各个子调用。
- 子调用的所在TM向TC注册分支事务,并执行本地prepare,并向TC报告执行结果。
- TC根据各分支事务的执行结果确定二阶段是执行commit或rollback。

Saga 模式
Saga模式是Seata提供的长事务解决方案。例如全局事务中涉及到外部系统,无法管理它的资源管理器,让它改造成TCC也不好实行,这时就可以采用此类方案。
在Saga模式中,业务流程中每个参与者都提交本地事务,当出现某一个参与者失败则补偿前面已经成功的参与者,一阶段正向服务和二阶段补偿服务都由业务开发实现。

XA模式
借助Seata框架的XA模式,会使XA方案更简单易用。使用前提:需要分支数据库支持XA 事务,应用为 Java应用,且使用JDBC访问数据库。
- 执行阶段:业务sql在XA分支中执行,由分支事务的RM管理器管理,然后执行XA prepare。
- 完成阶段:TM根据各个分支执行结果通过TC通知各个分支执行XA commit或者XA rollback。

Seata 各模式对比

工作流程

1.TM 请求 TC,开始一个新的全局事务,TC 会为这个全局事务生成一个 XID。
2.XID 在微服务调用链路的上下文中传播。
3.RM 开始执行分支事务,RM首先解析这条SQL语句,生成对应的UNDO_LOG,UNDO_LOG表中记录了分支ID,全局事务ID,以及事务执行的redo和undo数据以供二阶段恢复。
{"branchId": 641789253,//分支事务ID"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"//全局ID}
4.RM在同一个本地事务中执行业务SQL和UNDO_LOG数据的插入。在提交这个本地事务前,RM会向TC申请关于这条记录的全局锁。如果申请不到,则说明有其他事务也在对这条记录进行操作,因此它会在一段时间内重试,重试失败则回滚本地事务,并向TC汇报本地事务执行失败。
5.RM在事务提交前,申请到了相关记录的全局锁,因此直接提交本地事务,并向TC汇报本地事务执行成功。此时全局锁并没有释放,全局锁的释放取决于二阶段是提交命令还是回滚命令。
6.TC根据所有的分支事务执行结果,向RM下发提交或回滚命令。
7.RM如果收到TC的提交命令,首先立即释放相关记录的全局锁,然后把提交请求放入一个异步任务的队列中,马上返回提交成功的结果给 TC。异步队列中的提交请求真正执行时,只是删除相应 UNDO LOG 记录而已。
8.RM如果收到TC的回滚命令,则会开启一个本地事务,通过 XID 和 Branch ID 查找到相应的 UNDO LOG 记录。将 UNDO LOG 中的后镜与当前数据进行比较,如果有不同,说明数据被当前全局事务之外的动作做了修改。这种情况,需要根据配置策略来做处理。否则,根据 UNDO LOG 中的前镜像和业务 SQL 的相关信息生成并执行回滚的语句并执行,然后提交本地事务达到回滚的目的,最后释放相关记录的全局锁。
隔离级别
应用
1)nacos安装 https://github.com/alibaba/nacos/releases,以standalone模式启动
1)seata服务端安装 https://github.com/seata/seata/releases
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-seata</artifactId><version>2.1.1.RELEASE</version><scope>compile</scope><exclusions><exclusion><groupId>io.seata</groupId><artifactId>seata-all</artifactId></exclusion></exclusions></dependency><dependency><groupId>io.seata</groupId><artifactId>seata-all</artifactId><version>1.4.1</version></dependency>
# springboot 整合单体 TC server 配置。
server:
port: 18080
seata:
config:
type: nacos
application-id: springboot-seata
registry:
type: nacos
# seata 事务组编号 用于TC集群名
tx-service-group: springboot-seata-group
spring:
application:
name: seata-distributed-transaction
datasource:
dynamic:
datasource:
# 设置 账号数据源配置
account-ds:
driver-class-name: com.mysql.cj.jdbc.Driver
password: 123456
url: jdbc:mysql://127.0.0.1:3306/accountdb?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&useSSL=false
username: root
# 设置 订单数据源配置
order-ds:
driver-class-name: com.mysql.cj.jdbc.Driver
password: 123456
url: jdbc:mysql://127.0.0.1:3306/orderdb?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&useSSL=false
username: root
# 设置商品 数据源配置
product-ds:
driver-class-name: com.mysql.cj.jdbc.Driver
password: 123456
url: jdbc:mysql://127.0.0.1:3306/productdb?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&useSSL=false
username: root
# 设置默认数据源或者数据源组 默认值即为master
primary: order-ds # 默认指定一个数据源
# 开启对 seata的支持
seata: true
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;
@GlobalTransactional
public void purchase(String userId, String commodityCode, int orderCount) {
storageFeignClient.deduct(commodityCode, orderCount);
orderFeignClient.create(userId, commodityCode, orderCount);
}
重要机制
(1)全局事务的回滚是如何实现的呢?
Seata 有一个重要的机制:回滚日志。
每个分支事务对应的数据库中都需要有一个回滚日志表 UNDO_LOG,在真正修改数据库记录之前,都会先记录修改前的记录值,以便之后回滚。
在收到回滚请求后,就会根据 UNDO_LOG 生成回滚操作的 SQL 语句来执行。
如果收到的是提交请求,就把 UNDO_LOG 中的相应记录删除掉。
(2)RM 是怎么自动和 TC 交互的?
是通过监控拦截JDBC实现的,例如监控到开启本地事务了,就会自动向 TC 注册、生成回滚日志、向 TC 汇报执行结果。
(3)二阶段回滚失败怎么办?
例如 TC 命令各个 RM 回滚的时候,有一个微服务挂掉了,那么所有正常的微服务也都不会执行回滚,当这个微服务重新正常运行后,TC 会重新执行全局回滚。
