分布式事物seata篇(搭建使用以及理解)

什么是seata

Seata(Simple Extensible Autonomous Transaction Architecture) 是阿里巴巴一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。

Seata搭建使用

(1)服务端

①下载seata服务(启动包):
首先访问:https://github.com/seata/seata/releases
下载我们需要使用的seata1.2.0服务
https://github.com/seata/seata/releases/download/v1.2.0/seata-server-1.2.0.tar.gz
②建表
创建seata数据库,根据官方文档介绍找到sql文件,并创建
全局事务会话信息由3块内容构成,全局事务–>分支事务–>全局锁,对应表global_table、branch_table、lock_table
在这里插入图片描述
在这里插入图片描述
③修改存储模式
位置:seata–>conf–>file.conf,修改store.mode=“db”,将默认的file修改问db,一般情况我们都会使用db去存储,修改配置文件file.cong, db 以及db配置改为自己得配置 (可以使用mysql或者oracle,我使用mysql)
在这里插入图片描述
④配置连接注册中心
Nacos,nacos提前是以及配置好得启动即可
在这里插入图片描述
配置好后启动就可在nacos中看到,启动得seata服务
在这里插入图片描述

⑤Seata使用nacos配置中心
Seata中readme文件写到 config-center

用于存放各种配置中心的初始化脚本,执行时都会读取 config.txt配置文件,并写入配置中心

  • nacos: 用于向 Nacos 中添加配置
  • zk: 用于向 Zookeeper 中添加配置,脚本依赖 Zookeeper 的相关脚本,需要手动下载;ZooKeeper相关的配置可以写在 zk-params.txt 中,也可以在执行的时候输入
  • apollo: 向 Apollo 中添加配置,Apollo 的地址端口等可以写在 apollo-params.txt,也可以在执行的时候输入
  • etcd3: 用于向 Etcd3 中添加配置
  • consul: 用于向 consul 中添加配置
    1)修改config.txt配置文件
    Config文件中默认会很多,我们只留自己有用得就好,没用得先删除(认识得留着,不认识得先删除),本人只留了db配置以及group名称,如下图
    在这里插入图片描述
    2)修改nacos脚本,并运行
    修改nacos-config.sh,修改文件中下图标记位置为自己nacos配置
    在这里插入图片描述
    修改后执行脚本,执行成功后在打开nacos,配置是否存在
    在这里插入图片描述

(2)客户端

①建表
其他各服务数据库添加undo_log表
在这里插入图片描述
②Pom.xml 导入Jar. 每个服务都需导入Seata相关包
下边pom中排除com.alibaba.cloud中的seata是因为他默认使用的是0.9版本,而我搭建的1.2.0版本所以需要排除,自己单独引入

<dependency>
   <groupId>com.alibaba.cloud</groupId>
   <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
   <version>2.2.1.RELEASE</version>
   <exclusions>
       <exclusion>
           <groupId>io.seata</groupId>
           <artifactId>seata-all</artifactId>
       </exclusion>
       <exclusion>
           <groupId>io.seata</groupId>
           <artifactId>seata-spring-boot-starter</artifactId>
       </exclusion>
   </exclusions>
  </dependency>
  <dependency>
      <groupId>io.seata</groupId>
      <artifactId>seata-spring-boot-starter</artifactId>
      <version>1.2.0</version>
  </dependency>

③配置文件
这是本地配置连接nacos

spring.application.name=mworkflow
spring.profiles.active=gyc
spring.cloud.nacos.config.server-addr=xxx.xxx.xx.xxx:8848
spring.cloud.nacos.config.file-extension=properties
spring.cloud.nacos.config.namespace=ad0b27bd-d258-4b58-b4db-d4ac8b809d14
spring.cloud.nacos.config.group=MWORKFLOW
spring.cloud.nacos.discovery.server-addr=xxx.xxx.xx.xxx:8848
spring.cloud.nacos.discovery.namespace=ad0b27bd-d258-4b58-b4db-d4ac8b809d14
spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect

Nacos配置中心的加入seata配置

seata.application-id=${spring.application.name}
seata.tx-service-group=default
seata.service.vgroupMapping.default=default
seata.service.grouplist.default=127.0.0.1:8091
seata.enable-auto-data-source-proxy= true  //是否代理 

在这里插入图片描述
④注意事项
seata.tx-service-group的值my_test_tx_group必须跟nacos中的配置一样,可以在nacos配置中心手动添加
seata 如果用的是1.2.0则要求springcloud的版本为Hoxton.SR3,低springcloud版本启动可能会报错
seata RM端需要创建undo_log表
⑤启动Nacos, Seata后, 启动服务
若Seata控制台输出服务相关信息及数据库信息则连接成功, 如下:
在这里插入图片描述
BUG断点测试, 拦截过程中查看Seata数据库表和服务undo_log表
若表存在数据, 如:
在这里插入图片描述

seata理解

(1)术语

TC(Server端)为单独服务端部署
TM和RM(Client端)由业务系统集成。

①TC - 事务协调者
维护全局和分支事务的状态,驱动全局事务提交或回滚。
②TM - 事务管理器
定义全局事务的范围:开始全局事务、提交或回滚全局事务。(事务的发起者,并且最终与tc通信告诉事务的成功与否)
③RM - 资源管理器
管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。(事物的参与者,我们一个一个的微服务)

(2)AT执行步骤

简要说说整个全局事务的执行步骤:
在这里插入图片描述

TM 请求 TC 开启一个全局事务。设置超时时间等属性,以避免拿不到锁时,无限的等待,TC 会生成一个 XID 作为该全局事务的编号。
XID,会在微服务的调用链路中传播,保证将多个微服务的子事务关联在一起。
RM 请求 TC 将本地事务注册为全局事务的分支事务,通过全局事务的 XID 进行关联。
TM 请求 TC 告诉 XID 对应的全局事务是进行提交还是回滚。
TC 驱动 RM 们将 XID 对应的自己的本地事务进行提交还是回滚。

3)Tm处理流程

首先需要关注的是@GlobalTransactional注解的作用,它是在GlobalTransactionalInterceptor中被拦截处理在这里插入图片描述
调用了TransactionalTemplate.execute在这里插入图片描述
TransactionalTemplate定义了TM对全局事务处理的标准步骤,在这里插入图片描述
Seata在GlobalTransactionScanner中进行了TM和RM初始化

private void initClient() {
    //init TM
    TMClient.init(applicationId, txServiceGroup);
    //init RM
    RMClient.init(applicationId, txServiceGroup);
    //Register Spring ShutdownHook
    registerSpringShutdownHook();}

其中TMClient的初始化如下:

public static void init(String applicationId, String transactionServiceGroup) {
    TmRpcClient tmRpcClient = TmRpcClient.getInstance(applicationId, transactionServiceGroup);
    tmRpcClient.init();}

其中初始化了一个TmRpcClient对象,它的init方法中,会启动与Server的重连接线程(每5秒),以及消息发送线程和超时线程。重连接事,按照配置方式,寻找Server信息,比如采用file形式,那么首先从file.conf中根据分组名称(service_group)找到集群名称(cluster_name),再根据集群名称找到fescar-server集群ip端口列表,然后从ip列表中选择一个用netty进行连接。

RmClient的初始化如下:

public static void init(String applicationId, String transactionServiceGroup) {
    RmRpcClient rmRpcClient = RmRpcClient.getInstance(applicationId, transactionServiceGroup);
    rmRpcClient.setResourceManager(DefaultResourceManager.get());
    rmRpcClient.setClientMessageListener(new RmMessageListener(DefaultRMHandler.get()));
    rmRpcClient.init();}

其中,主要工作为:
创建一个RmRpcClient对象
RmRpcClient对象设置ResourceManager;
RmRpcClient对象设置消息Listener;
RmRpcClient对象初始化

初始化阶段总结:
1、Spring启动时,初始化了2个客户端TmClient、RmClient
2、TmClient与Server通过Netty建立连接并发送消息
3、RmClient与Server通过Netty建立连接,负责接收二阶段提交、回滚消息并在回调器(RmHandler)中做处理

4)详细说明(单体服务RM具体的执行流程。)

在这里插入图片描述

a.第一步:
解析sql语句,得到 SQL 的类型(UPDATE),表(product),条件(where name = ‘TXC’)等相关的信息。
b.第二步:
查询老数据,根据上面的where语句sql,去数据库查询原始的数据。
如 select * from product where name = ‘TXC’;得到原始的数据,如该行id=1,然后记录下来。
c.第三步:
执行第一步的sql语句,即执行update,修改数据库的该记录的值。
d.第四步:
查询修改后的值,select * from product where id =1.得到该行值,记录下来。
e.第五步:
插入回滚日志,将老值、新值以及sql语句组成一个将来可用于回滚的日志,插入到UNDO_LOG表。如下图
在这里插入图片描述

{
“branchId”: 641789253,
“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”

f.第六步:
向TC server注册分支,申请product表,id=1的行的全局锁。注意,这个全局锁是相对于所有可能的同时在执行的分布式事务而言的。一锁之前,任何其他的分布式事务,不能修改该数据。旦某个分支,获取了该记录的全局锁,在解
g.第七步:
本地事务提交,将自己的本地事务、和前面的UNDO LOG一起提交。
h.第八步:
将本地事务提交的结果上报给TC server。如成功、失败。
此时TC会陆续收到各个分支的执行结果,在各分支全部提交完毕后,TC会下发最终结果给各分支。
i.开始第二阶段。
成功的情况:

分支收到了TC下发的成功请求,立马返回我已OK的结果给TC,然后异步执行删除UNDO LOG的操作。因为成功了,所以用来回滚的UNDO LOG就没意义了,异步删除掉就好。
在这里插入图片描述

失败的情况:
1 分支收到了TC下发的失败请求,开始执行回滚逻辑。
2 通过 XID 和 Branch ID 查找到相应的 UNDO LOG 记录。
3 数据校验:拿 UNDO LOG 中的后镜与当前数据进行比较,如果有不同,说明数据被当前全局事务之外的动作做了修改。这种情况,需要根据配置策略来做处理
4 根据 UNDO LOG 中的前镜像和业务 SQL 的相关信息生成并执行回滚的语句。
update product set name = ‘TXC’ where id = 1;
5 提交本地事务。并把本地事务的执行结果(即分支事务回滚的结果)上报给 TC。在这里插入图片描述

5)结论:

可以看到,整体来说,这个分布式事务是比较迅速的,在不等待全局锁的情况下,基本和本地事务没什么区别。回滚时,也不依赖数据库本身的回滚能力,都由自己业务来实现回滚操作。
总结一下
1.对业务无侵入:即减少技术架构上的微服务化所带来的分布式事务问题对业务的侵入
2.AT 模式的工作流程分为两阶段。一阶段进行业务 SQL 执行,并通过 SQL 拦截、SQL 改写等过程生成修改数据前后的快照(Image),并作为 UndoLog 和业务修改在同一个本地事务中提交。如果一阶段成功那么二阶段仅仅异步删除刚刚插入的 UndoLog;如果二阶段失败则通过 UndoLog 生成反向 SQL 语句回滚一阶段的数据修改。
2.AT 模式是无侵入的分布式事务解决方案,适用于不希望对业务进行改造的场景,几乎 0 学习成本。

问题总结

  1. 怎么使用Seata框架,来保证事务的隔离性?
    因seata一阶段本地事务已提交,为防止其他事务脏读脏写需要加强隔离。
    1.脏读 select语句加for update,代理方法增加@GlobalLock或@GlobalTransaction
    2.脏写 必须使用@GlobalTransaction
    3.注:如果你查询的业务的接口没有GlobalTransactional 包裹,也就是这个方法上压根没有分布式事务的需求,这时你可以在方法上标注@GlobalLock 注解,并且在查询语句上加 for update。 如果你查询的接口在事务链路上外层有GlobalTransactional注解,那么你查询的语句只要加for update就行。设计这个注解的原因是在没有这个注解之前,需要查询分布式事务读已提交的数据,但业务本身不需要分布式事务。 若使用GlobalTransactional注解就会增加一些没用的额外的rpc开销比如begin 返回xid,提交事务等。GlobalLock简化了rpc过程,使其做到更高的性能。
  2. 脏数据回滚失败如何处理?
    1.脏数据需手动处理,根据日志提示修正数据或者将对应undo删除(可自定义实现FailureHandler做邮件通知或其他)
    2.关闭回滚时undo镜像校验,不推荐该方案。
  3. 使用 AT 模式需要的注意事项有哪些 ?
    1.必须使用代理数据源,有 3 种形式可以代理数据源:
    2.依赖 seata-spring-boot-starter 时,自动代理数据源,无需额外处理。
    3.依赖 seata-all 时,使用 @EnableAutoDataSourceProxy (since 1.1.0) 注解,注解参数可选择 jdk 代理或者 cglib 代理。
    4.依赖 seata-all 时,也可以手动使用 DatasourceProxy 来包装 DataSource。
    5.配置 GlobalTransactionScanner,使用 seata-all 时需要手动配置,使用 seata-spring-boot-starter 时无需额外处理。
    6.业务表中必须包含单列主键,若存在复合主键,(暂不支持,建议先建一列自增id主键,原复合主键改为唯一键来规避下)。
    7.每个业务库中必须包含 undo_log 表,若与分库分表组件联用,分库不分表。
    8.跨微服务链路的事务需要对相应 RPC 框架支持,目前 seata-all 中已经支持:Apache Dubbo、Alibaba Dubbo、sofa-RPC、Motan、gRpc、httpClient,对于 Spring Cloud 的支持,引用 spring-cloud-alibaba-seata。
    目前AT模式支持的数据库有:MySQL、Oracle、PostgreSQL和 TiDB。
  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值