分布式事务
分布式事务
1. 分布式事务场景
当项目是微服务架构时,一个功能需求可能需要多个微服务共同完成,这个过程就难免会操作不同服务下的不同数据库,那么如何保证数据库的ACID的性质呢?
除了微服务架构,只要是分布式架构就会面临同样的问题,此时无法通过mysql提供的事务功能解决,此时就需要用到分布式事务解决方案。
2. Seata
2.1 认识Seata
Seata是阿里提供的开源分布式事务框架(https://seata.apache.org/zh-cn/docs/overview/what-is-seata/)。
在Seata的事务管理中,有三个角色:
- TC(Transaction Coordinator) - 事务协调者:维护全局和分支事务的状态,协调全局事务提交或者回滚;
- TM(Transaction Manager) - 事务管理器:定义全局事务的范围,开始全局事务、提交或回滚全局事务;
- RM(Resource Manager) - 资源管理器:管理分支事务,于TC交互以注册分支事务和报告分支事务的状态,驱动分支事务提交或者回滚。
TC相当于事务管理的服务器,TM和RM是事务管理的客户端,Seata事务管理框架:
要使用分布式事务,需要以下三步:部署TC服务;微服务集成Seata;选择事务处理模式并配置
2.2 部署TC服务
步骤一:准备数据库表,保存Seata的临时文件,也可以选择redis等数据库
步骤二:准备配置文件
步骤三:在docker中部署
1
2
3
4
5
6
7
8
9docker run --name seata \
-p 8099:8099 \
-p 7099:7099 \
-e SEATA_IP=192.168.223.101 \
-v ./seata:/seata-server/resources \
--privileged=true \
--network hm-net \
-d \
seataio/seata-server:1.5.2
2.3 微服务集成Seata
分布式事务中涉及到的所有微服务都需要集成Seata
- 步骤一:引入依赖,重点是seata依赖,而前两个依赖是为了共享配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15<!--统一配置管理-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!--读取bootstrap文件-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<!--seata-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency> - 步骤二:添加seata配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14seata:
registry: # TC服务注册中心的配置,微服务根据这些信息去注册中心获取tc服务地址
type: nacos # 注册中心类型 nacos
nacos:
server-addr: 192.168.150.101:8848 # nacos地址
namespace: "" # namespace,默认为空
group: DEFAULT_GROUP # 分组,默认是DEFAULT_GROUP
application: seata-server # seata服务名称
username: nacos
password: nacos
tx-service-group: hmall # 事务组名称
service:
vgroup-mapping: # 事务组与tc集群的映射关系
hmall: "default" - 步骤三:在bootstrap.yaml中添加共享配置项
- 步骤四:在微服务的数据库中添加一个表,用于保存事务一些中间数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14-- 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'; - 步骤五:在全局事务的方法中添加注解@GlobalTransactional
3. 分布式事务解决方案
在Seata中提供了4中分布式事务解决方案,这里我们只介绍前两种:
- XA
- AT
- TCC
- SAGA
3.1 XA模式
XA是一种分布式事务处理方案的标准,几乎所有主流的数据库都对XA规范提供了支持。接下来解释Seata的XA模型。
XA模型分为两个阶段
RM 一阶段的工作:
- 注册分支事务到TC
- 执行分支业务sql但不提交
- 报告执行状态到TC
TC 二阶段的工作:
- TC检测各分支事务执行状态
- 如果都成功,通知所有RM提交事务
- 如果有失败,通知所有RM回滚事务
- TC检测各分支事务执行状态
RM 二阶段的工作:
- 接收TC指令,提交或回滚事务
优缺点:
- 优点
- 保证了事务的强一致性
- 常用的数据都支持,实现简单
- 缺点
- 一阶段执行完sql后,不能提交事务,需要一直占用资源,导致性能差
- 依赖关系型数据库实现事务
实现步骤
- 步骤一:在共享的seata配置文件中配置模式
1
2seata:
data-source-proxy-mode: XA - 步骤二:使用@GlobalTransactional注解标记全局事务的入口
3.2 AT模式
AT模式依旧是两阶段模型,但是弥补了资源释放慢、性能差的缺陷。
基本流程
- 阶段一RM的工作:
- 注册分支事务
- 记录undo-log(数据快照)
- 执行业务sql并提交
- 报告事务状态
- 阶段二提交时RM的工作:
- 删除undo-log即可
- 阶段二回滚时RM的工作:
- 根据undo-log恢复数据到更新前
在AT模式中,分支事务完成sql语句后可以直接提交,但是在更新sql前需要记录 快照(前面数据库添加的undo-log表) 并保存,若第二阶段判断全局事务失败,则根据快照进行恢复。全局事务完成后,无论是否成功,都会删除快照
优缺点
- 优点
- 阶段一不锁定资源,因此效率较高
- 不依赖关系型数据库的事务,回滚机制依赖于快照
- 缺点
- 在第一阶段和第二阶段间可能会有数据不一致的现象,但是最终一致
实现步骤
- 步骤一:修改共享配置seata钟大哥事务解决方案模型
1
2seata:
data-source-proxy-mode: AT - 步骤二:使用@GlobalTransactional注解标记全局事务的入口
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 BBThc_z!