分布式事务解决方案 - Seata

分布式事务解决方案 - Seata

在分布式系统中,如果一个业务需要多个服务合作完成,而且每一个服务都有事务,多个事务必须同时成功或失败,这样的事务就是分布式事务。其中的每个服务的事务就是一个分支事务。整个业务称为全局事务

1579502a79511d60e37b2abf29820423a2aa4379.png

应该是都成功或者都失败才对

分布式事务解决思路:Seata架构

相互之间不知道对方成没成功,想办法让事务分支之间感知到彼此的事务状态,才能保证状态一致。

Seata架构

  • TC - 事务协调者
  • TM - 事务管理器
  • RM - 资源管理器
    f1d7c0086b259af70e1f3c03197d6298c7bc7b10.png

Dcoker部署Seata

  1. 注意:部署之前先看有哪些网桥
1
docker network ls
  1. 然后看自己的mysql容器是不是在这个网桥下:
1
docker inspect mysql

我这里查出来的是在hmall下
8394b4207e48c570b92b90d4641404a2a977cb5d.png

  1. 再查以下nacos容器,我这里查出来是不在hmall里的
    a4ee6597718096f9b12ff349bb2465815ad39541.png
    如果不在就可以把已经存在的加进来:
1
docker network connect hmall nacos

最后执行如下命令即可完成seata的容器化部署

1
2
3
4
5
6
7
8
9
docker run --name seata \
-p 8099:8099 \
-p 7099:7099 \
-e SEATA_IP=192.168.100.0 \ # 改成你的虚拟机IP
-v ./seata:/seata-server/resources \
--privileged=true \
--network hmall \ # 改成你Nacos所在的默认网桥(原命令是hm-net,你没有这个自定义网络)
-d \
seataio/seata-server:1.5.2

部署完成后就可以访问:你的虚拟机地址:7099 访问,用户名密码默认为:admin
9d306ee87f8f49d2a7314984da97514c95b8d769.png

微服务集成Seata

  1. 引依赖
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>
  1. 在application.yml中配置TC服务地址
1
2
3
4
5
6
7
8
9
10
11
12
13
14
seata:
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"

这些配置呢所有事务的参与者都要去配啊,所以我们抽取到一个nacos中的共享配置
这时候就有同学问了,这么多依赖直接放在common不就行了吗,非也非也
并不是每个微服务都会有分布式事务,所以哪个微服务用到就往哪个引入就行了

Seata解决分布式事务提供了不同的模式

XA模式

原理:当微服务执行sql的时候还没有提交,会先上锁等待,等待TC检查事务状态之后才会解锁提交,
确保大家是同时成功同时失败的,因为有两阶段

  • 一阶段的工作
    ①RM注册分支事务到TC
    ②RM执行分支业务sql但不提交
    ③RM报告执行状态到TC
  • 二阶段的工作
    ①TC检测各分支事务执行状态
    a. 如果有成功,通知所有RM事务提交事务
    b. 如果有失败,通知所有RM事务回滚事务
    ②RM接收TC指令,提交或回滚事务
    1181bbee7c8b6b56132694bfbbebc9a79139baf3.png
    优点:确保了ACID的特性,强一致
    缺点: 上锁等待(双刃剑),有些业务是串行执行,每个分支都耗时,所以第一个分支久等了很久,就降低了性能。依赖于关系型数据库实现事务,如果有些数据库不支持XA模式,那就很尴尬了。

实现XA模式

  1. 修改application.yml共享文件(每个参与事务的微服务),开启XA模式
1
2
seata:
data-source-proxy-mode: XA
  1. 给发起全局事务的入口方法添加@GloablTransactional注解
    51f5372ed1a1bf88b90f9ddaff01f4ae43c92bf9.png

AT模式- 最终一致

Seata主推的是AT模式,AT模式同样是分阶段提交的事务模型,不过弥补了XA模式中资源锁定周期过长的缺陷。

在进行RM操作前都是和XA模式一致的

阶段一RM的工作:
① 注册分支事务
② 记录undo-log(数据快照)
③ 执行业务sql并提交
④ 报告事务状态

阶段二提交时RM的工作:
① 删除undo-log即可
阶段二回滚时RM的工作:
① 根据undo-log恢复数据到更新前
ee82a8e0376a4c47edcbc8af6801c77ef1f865f1.png
鱼和熊掌不可兼得

实现AT模式

  1. 添加需要分布式事务对应微服务的undo-log到微服务对应的数据库中
  2. 修改application.yml文件,将事务模式修改为AT模式