文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Spring Cloud Alibaba全家桶(九)——分布式事务组件Seata

2023-09-03 06:19

关注

前言

在这里插入图片描述

本文小新为大家带来 分布式事务组件Seata 相关知识,具体内容包括分布式事务简介(包括:事务简介本地事务分布式事务典型场景分布式事务理论基础分布式事务解决方案),分布式事务Seata使用(包括:Seata是什么Seata的三大角色Seata的设计思路Seata的设计亮点Seata存在的问题),Seata快速开始(包括:Seata Server(TC)环境搭建业务系统集成Client)等进行详尽介绍~

不积跬步,无以至千里;不积小流,无以成江海。每天进步一点点,在成为强者的路上,小新与大家共同成长!

📌博主主页:小新要变强 的主页
👉Java全栈学习路线可参考:【Java全栈学习路线】最全的Java学习路线及知识清单,Java自学方向指引,内含最全Java全栈学习技术清单~
👉算法刷题路线可参考:算法刷题路线总结与相关资料分享,内含最详尽的算法刷题路线指南及相关资料分享~
👉Java微服务开源项目可参考:企业级Java微服务开源项目(开源框架,用于学习、毕设、公司项目、私活等,减少开发工作,让您只关注业务!)

↩️本文上接:Spring Cloud Alibaba全家桶(八)——Sentinel规则持久化


目录

分布式事务组件Seata

在这里插入图片描述

一、分布式事务简介

1️⃣事务简介

事务(Transaction) 是访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。在关系数据库中,一个事务由一组SQL语句组成。事务应该具有4个属性:原子性、一致性、隔离性、持久性。 这四个属性通常称为ACID特性。

2️⃣本地事务

大多数场景下,我们的应用都只需要操作单一的数据库,这种情况下的事务称之为本地事务
(Local Transaction)
。本地事务的ACID特性是数据库直接提供支持。本地事务应用架构如下所
示:

在这里插入图片描述

在JDBC编程中,我们通过java.sql.Connection对象来开启、关闭或者提交事务。代码如下所
示:

Connection conn = ... //获取数据库连接conn.setAutoCommit(false); //开启事务try{  //...执行增删改查sql  conn.commit(); //提交事务}catch (Exception e) {  conn.rollback();//事务回滚}finally{  conn.close();//关闭链接}

3️⃣分布式事务典型场景

当下互联网发展如火如荼,绝大部分公司都进行了数据库拆分和服务化(SOA)。在这种情况下,完
成某一个业务功能可能需要横跨多个服务,操作多个数据库。这就涉及到到了分布式事务,用需要操作的资源位于多个资源服务器上,而应用需要保证对于多个资源服务器的数据的操作,要么全部成功,要么全部失败。本质上来说,分布式事务就是为了保证不同资源服务器的数据一致性。

典型的分布式事务场景:

跨库事务指的是,一个应用某个功能需要操作多个库,不同的库中存储不同的业务数据。笔者见过
一个相对比较复杂的业务,一个业务中同时操作了9个库。下图演示了一个服务同时操作2个库的情
况:

在这里插入图片描述

通常一个库数据量比较大或者预期未来的数据量比较大,都会进行水平拆分,也就是分库分表。如
下图,将数据库B拆分成了2个库:

在这里插入图片描述

对于分库分表的情况,一般开发人员都会使用一些数据库中间件来降低sql操作的复杂性。如,对于sql:insert into user(id,name) values (1,"张三"),(2,"李四")。这条sql是操作单库的语法,单库情况下,可以保证事务的一致性。

但是由于现在进行了分库分表,开发人员希望将1号记录插入分库1,2号记录插入分库2。所以数据库中间件要将其改写为2条sql,分别插入两个不同的分库,此时要保证两个库要不都成功,要不都
失败,因此基本上所有的数据库中间件都面临着分布式事务的问题。

微服务架构是目前一个比较一个比较火的概念。例如上面笔者提到的一个案例,某个应用同时操作了9个库,这样的应用业务逻辑必然非常复杂,对于开发人员是极大的挑战,应该拆分成不同的独立服务,以简化业务逻辑。拆分后,独立服务之间通过RPC框架来进行远程调用,实现彼此的通信。下图演示了一个3个服务之间彼此调用的架构:

在这里插入图片描述

Service A完成某个功能需要直接操作数据库,同时需要调用Service B和Service C,而Service B 又同时操作了2个数据库,Service C也操作了一个库。需要保证这些跨服务的对多个数据库的操作要不都成功,要不都失败,实际上这可能是 典型的分布式事务场景。

小结: 上述讨论的分布式事务场景中,无一例外的都直接或者间接的操作了多个数据库。如何保证事务的ACID特性,对于分布式事务实现方案而言,是非常大的挑战。同时,分布式事务实现方案还必须要考虑性能的问题,如果为了严格保证ACID特性,导致性能严重下降,那么对于一些要求快速响应的业务,是无法接受的。

4️⃣分布式事务理论基础

解决分布式事务,也有相应的规范和协议。分布式事务相关的协议有2PC、3PC。

由于三阶段提交协议3PC非常难实现,目前市面主流的分布式事务解决方案都是2PC协议。2PC具有普适性——协议一样的存在,目前绝大多数分布式解决方案都是以两阶段提交协议2PC为基础的。

2PC(两阶段提交,Two-Phase Commit)

顾名思义,分为两个阶段:Prepare 和 Commit。

🍀(1)Prepare:提交事务请求

基本流程如下图:

在这里插入图片描述

🍀(2)Commit:执行事务提交

执行事务提交分为两种情况,正常提交和回退。

正常提交事务

流程如下图:

在这里插入图片描述

中断事务

在执行 Prepare 步骤过程中,如果某些参与者执行事务失败、宕机或与协调者之间的网络中断,那么协调者就无法收到所有参与者的 YES 响应,或者某个参与者返回了 No 响应,此时,协调者就会进入回退流程,对事务进行回退。流程如下图红色部分(将 Commit 请求替换为红色的 Rollback 请求):

在这里插入图片描述

2PC 存在的问题:

5️⃣分布式事务解决方案

常见分布式事务解决方案:

他们有一个共同点,都是“两阶段(2PC)”。“两阶段”是指完成整个分布式事务,划分成两个步骤完成。
实际上,这四种常见的分布式事务解决方案,分别对应着分布式事务的四种模式:AT、TCC、Saga、XA;四种分布式事务模式,都有各自的理论基础,分别在不同的时间被提出;每种模式都有它的适用场景,同样每个模式也都诞生有各自的代表产品;而这些代表产品,可能就是我们常见的(全局事务、基于可靠消息、 大努力通知、TCC)。

下面我们分别来看4种模式(AT、TCC、Saga、XA)的分布式事务实现。

🍀(1)AT模式(auto transcation)

AT 模式是一种无侵入的分布式事务解决方案。

阿里seata框架,实现了该模式。

在 AT 模式下,用户只需关注自己的“业务 SQL”,用户的 “业务 SQL” 作为一阶段,Seata 框架会自动生成事务的二阶段提交和回滚操作。

在这里插入图片描述

AT 模式如何做到对业务的无侵入 :

在一阶段,Seata 会拦截“业务 SQL”,首先解析 SQL 语义,找到“业务 SQL”要更新的业务数据,在业务数据被更新前,将其保存成“before image”,然后执行“业务 SQL”更新业务数据,在业务数据更新之后,再将其保存成“after image”,最后生成行锁。以上操作全部在一个数据库事务内完成,这样保证了一阶段操作的原子性。

在这里插入图片描述

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

在这里插入图片描述

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

在这里插入图片描述

AT 模式的一阶段、二阶段提交和回滚均由 Seata 框架自动生成,用户只需编写“业务 SQL”,便能
轻松接入分布式事务,AT 模式是一种对业务无任何侵入的分布式事务解决方案。

🍀(2)TCC 模式(Try、Confirm and Cancel)

TCC 模式特点:

TCC 模式需要用户根据自己的业务场景实现 Try、Confirm 和 Cancel 三个操作;事务发起方在一阶段执行 Try 方式,在二阶段提交执行 Confirm 方法,二阶段回滚执行 Cancel 方法。

在这里插入图片描述

TCC 三个方法描述:

对于TCC模式的实践过程中要注意以下事项:

用户接入 TCC 模式,最重要的事情就是考虑如何将业务模型拆成 2 阶段,实现成 TCC 的 3 个方
法,并且保证 Try 成功 Confirm 一定能成功。相对于 AT 模式,TCC 模式对业务代码有一定的侵入
性,但是 TCC 模式无 AT 模式的全局行锁,TCC 性能会比 AT 模式高很多。

在这里插入图片描述

Cancel 接口设计时需要允许空回滚。在 Try 接口因为丢包时没有收到,事务管理器会触发回滚,这
时会触发 Cancel 接口,这时 Cancel 执行时发现没有对应的事务 xid 或主键时,需要返回回滚成功。让事务服务管理器认为已回滚,否则会不断重试,而 Cancel 又没有对应的业务数据可以进行回滚。

在这里插入图片描述

悬挂的意思是:Cancel 比 Try 接口先执行,出现的原因是 Try 由于网络拥堵而超时,事务管理器生成回滚,触发 Cancel 接口,而 终又收到了 Try 接口调用,但是 Cancel 比 Try 先到。按照前面允许空回滚的逻辑,回滚会返回成功,事务管理器认为事务已回滚成功,则此时的 Try 接口不应该执行,否则会产生数据不一致,所以我们在 Cancel 空回滚返回成功之前先记录该条事务 xid 或业务主键,标识这条记录已经回滚过,Try 接口先检查这条事务xid或业务主键如果已经标记为回滚成功过,则不执行 Try 的业务操作。

在这里插入图片描述

幂等性的意思是:对同一个系统,使用同样的条件,一次请求和重复的多次请求对系统资源的影响是一致的。因为网络抖动或拥堵可能会超时,事务管理器会对资源进行重试操作,所以很可能一个业务操作会被重复调用,为了不因为重复调用而多次占用资源,需要对服务设计时进行幂等控制,通常我们可以用事务 xid 或业务主键判重来控制。

🍀(3)saga模式

在这里插入图片描述

Saga 理论出自 Hector & Kenneth 1987发表的论文 Sagas。

saga模式的实现,是长事务解决方案。

Saga 是一种补偿协议,在 Saga 模式下,分布式事务内有多个参与者,每一个参与者都是一个冲正
补偿服务,需要用户根据业务场景实现其正向操作和逆向回滚操作。

在这里插入图片描述

如图:T1-T3都是正向的业务流程,都对应着一个冲正逆向操作C1-C3

分布式事务执行过程中,依次执行各参与者的正向操作,如果所有正向操作均执行成功,那么分布式事务提交。如果任何一个正向操作执行失败,那么分布式事务会退回去执行前面各参与者的逆向回滚操作,回滚已提交的参与者,使分布式事务回到初始状态。

Saga 正向服务与补偿服务也需要业务开发者实现。因此是业务入侵的。

Saga 模式下分布式事务通常是由事件驱动的,各个参与者之间是异步执行的,Saga 模式是一种长事务解决方案。

Saga 模式使用场景:

Saga模式的优势:

Saga模式的缺点:

与TCC实践经验相同的是,Saga 模式中,每个事务参与者的冲正、逆向操作,需要支持:

🍀(4)XA模式

XA是X/Open DTP组织(X/Open DTP group)定义的两阶段提交协议,XA被许多数据库(如
Oracle、DB2、SQL Server、MySQL)和中间件等工具(如CICS 和 Tuxedo)本地支持 。

X/Open DTP模型(1994)包括应用程序(AP)、事务管理器(TM)、资源管理器(RM)。

XA接口函数由数据库厂商提供。XA规范的基础是两阶段提交协议2PC。

JTA(Java Transaction API) 是Java实现的XA规范的增强版 接口。

在XA模式下,需要有一个[全局]协调器,每一个数据库事务完成后,进行第一阶段预提交,并通知协
调器,把结果给协调器。协调器等所有分支事务操作完成、都预提交后,进行第二步;第二步:协调器通知每个数据库进行逐个commit/rollback。其中,这个全局协调器就是XA模型中的TM角色,每个分支事务各自的数据库就是RM。

MySQL 提供的XA实现:https://dev.mysql.com/doc/refman/5.7/en/xa.html

XA模式下的开源框架有: atomikos,其开发公司也有商业版本。

XA模式缺点: 事务粒度大。高并发下,系统可用性低。因此很少使用。

🍀(5)(AT、TCC、Saga、XA)模式分析

四种分布式事务模式,分别在不同的时间被提出,每种模式都有它的适用场景:

总结:

分布式事务本身就是一个技术难题,业务中具体使用哪种方案还是需要不同的业务特点自行选择,但是我们也会发现,分布式事务会大大的提高流程的复杂度,会带来很多额外的开销工作,代码量上去了,业务复杂了,性能下跌了。

所以,当我们真实开发的过程中,能不使用分布式事务就不使用。

二、分布式事务Seata使用

1️⃣Seata是什么

Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供 AT、TCC、SAGA 和XA 事务模式,为用户打造一站式的分布式解决方案。AT模式是阿里首推的模式,阿里云上有商用版本的GTS(Global Transaction Service 全局事务服务)

官网: https://seata.io/zh-cn/index.html

源码: https://github.com/seata/seata

官方Demo: https://github.com/seata/seata-samples

2️⃣Seata的三大角色

在 Seata 的架构中,一共有三个角色:

其中,TC 为单独部署的 Server 服务端,TM 和 RM 为嵌入到应用中的 Client 客户端。

在 Seata 中,一个分布式事务的生命周期如下:

在这里插入图片描述

在这里插入图片描述

3️⃣Seata的设计思路

AT模式的核心是对业务无侵入,是一种改进后的两阶段提交,其设计思路如图。

第一阶段业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。核心在于对业务sql进行解析,转换成undolog,并同时入库,这是怎么做的呢?

先抛出一个概念DataSourceProxy代理数据源,通过名字大家大概也能基本猜到是什么个操作,后面做具体分析参考官方文档: https://seata.io/zh-cn/docs/dev/mode/at-mode.html

在这里插入图片描述

第二阶段分布式事务操作成功,则TC通知RM异步删除undolog。

在这里插入图片描述

分布式事务操作失败,TM向TC发送回滚请求,RM 收到协调器TC发来的回滚请求,通过 XID 和 Branch ID 找到相应的回滚日志记录,通过回滚记录生成反向的更新 SQL 并执行,以完成分支的回滚。

在这里插入图片描述

整体执行流程:

在这里插入图片描述

4️⃣Seata的设计亮点

相比与其它分布式事务框架,Seata架构的亮点主要有几个:

5️⃣Seata存在的问题

一条Update的SQL,则需要全局事务xid获取(与TC通讯)、before image(解析SQL,查询一次数据库)、after image(查询一次数据库)、insert undo log(写一次数据库)、before commit(与TC通讯,判断锁冲突),这些操作都需要一次远程通讯RPC,而且是同步的。另外undo log写入时blob字段的插入性能也是不高的。每条写SQL都会增加这么多开销,粗略估计会增加5倍响应时间。

为了进行自动补偿,需要对所有交易生成前后镜像并持久化,可是在实际业务场景下,这个是成功率有多高,或者说分布式事务失败需要回滚的有多少比率?按照二八原则预估,为了20%的交易回滚,需要将80%的成功交易的响应时间增加5倍,这样的代价相比于让应用开发一个补偿交易是否是值得?

相比XA,Seata 虽然在一阶段成功后会释放数据库锁,但一阶段在commit前全局锁的判定也拉长了对数据锁的占有时间,这个开销比XA的prepare低多少需要根据实际业务场景进行测试。全局锁的引入实现了隔离性,但带来的问题就是阻塞,降低并发性,尤其是热点数据,这个问题会更加严重。

Seata在回滚时,需要先删除各节点的undo log,然后才能释放TC内存中的锁,所以如果第二阶段是回滚,释放锁的时间会更长。

Seata的引入全局锁会额外增加死锁的风险,但如果出现死锁,会不断进行重试,最后靠等待全局锁超时,这种方式并不优雅,也延长了对数据库锁的占有时间。

三、Seata快速开始

1️⃣Seata Server(TC)环境搭建

Seata部署指南:https://seata.io/zh-cn/docs/ops/deploy-guide-beginner.html

🍀步骤一:下载安装包

下载地址:https://github.com/seata/seata/releases

目前最新的版本为1.6.1:

在这里插入图片描述

🍀步骤二:存储模式配置

启动包: seata–>conf–>application.yml,修改store.mode=“db或者redis”

源码: 根目录–>seata-server–>resources–>application.yml,修改store.mode=“db或者redis”

0以下版本:

启动包: seata–>conf–>file.conf,修改store.mode=“db或者redis”

源码: 根目录–>seata-server–>resources–>file.conf,修改store.mode=“db或者redis”

🍀步骤三:建表(仅db存储模式)

新建表: 可以去seata提供的资源信息中下载:https://github.com/seata/seata/blob/1.6.1/script/server/db/mysql.sql

🍀步骤四:修改数据库连接|redis属性配置

启动包: seata–>conf–>application.example.yml中附带额外配置,将其db|redis相关配置复制至application.yml,进行修改store.db或store.redis相关属性。

源码: 根目录–>seata-server–>resources–>application.example.yml中附带额外配置,将其db|redis相关配置复制至application.yml,进行修改store.db或store.redis相关属性。

0以下版本:

启动包: seata–>conf–>file.conf,修改store.db或store.redis相关属性。

源码: 根目录–>seata-server–>resources–>file.conf,修改store.db或store.redis相关属性。

🍀步骤五:启动

码启动: 执行ServerApplication.java的main方法

命令启动:

seata-server.sh -h 127.0.0.1 -p 8091 -m db

0以下版本

源码启动: 执行Server.java的main方法

命令启动:

seata-server.sh -h 127.0.0.1 -p 8091 -m db -n 1 -e test

命令启动参数:

-h: 注册到注册中心的ip-p: Server rpc 监听端口-m: 全局事务会话信息存储模式,file、db、redis,优先读取启动参数 (Seata-Server 1.3及以上版本支持redis)-n: Server node,多个Server时,需区分各自节点,用于生成不同区间的transactionId,以免冲突-e: 多环境配置参考 http://seata.io/en-us/docs/ops/multi-configuration-isolation.html

2️⃣业务系统集成Client

🍀步骤一:添加seata依赖(建议单选)

🍀步骤二:undo_log建表、配置参数(仅AT模式)

🍀步骤三:数据源代理(不支持自动和手动配置并存)

(1)如果使用seata-all:

1.1.0: seata-all取消属性配置,改由注解@EnableAutoDataSourceProxy开启,并可选择jdk proxy或者cglib proxy1.0.0: client.support.spring.datasource.autoproxy=true0.9.0: support.spring.datasource.autoproxy=true

如果采用XA模式,@EnableAutoDataSourceProxy(dataSourceProxyMode = "XA")

@Primary@Bean("dataSource")public DataSource dataSource(DataSource druidDataSource) {    //AT 代理 二选一    return new DataSourceProxy(druidDataSource);    //XA 代理    return new DataSourceProxyXA(druidDataSource)}

(2)如果使用seata-starter

application.yml:

seata:  data-source-proxy-mode: XA

application.yml:

seata:  enable-auto-data-source-proxy: false

🍀步骤四:初始化GlobalTransactionScanner

手动:

       @Bean       public GlobalTransactionScanner globalTransactionScanner() {           String applicationName = this.applicationContext.getEnvironment().getProperty("spring.application.name");           String txServiceGroup = this.seataProperties.getTxServiceGroup();           if (StringUtils.isEmpty(txServiceGroup)) {               txServiceGroup = applicationName + "-fescar-service-group";               this.seataProperties.setTxServiceGroup(txServiceGroup);           }              return new GlobalTransactionScanner(applicationName, txServiceGroup);       }

自动:

引入seata-spring-boot-starter、spring-cloud-starter-alibaba-seata等jar

🍀步骤五:实现xid跨服务传递

🍀业务使用——注解拦截

全局事务:

@GetMapping(value = "testCommit")@GlobalTransactionalpublic Object testCommit(@RequestParam(name = "id",defaultValue = "1") Integer id,    @RequestParam(name = "sum", defaultValue = "1") Integer sum) {    Boolean ok = productService.reduceStock(id, sum);    if (ok) {        LocalDateTime now = LocalDateTime.now();        Orders orders = new Orders();        orders.setCreateTime(now);        orders.setProductId(id);        orders.setReplaceTime(now);        orders.setSum(sum);        orderService.save(orders);        return "ok";    } else {        return "fail";    }}

TCC:

@TwoPhaseBusinessAction(name = "beanName", commitMethod = "commit", rollbackMethod = "rollback", useTCCFence = true)public void insert(@BusinessActionContextParameter(paramName = "params") Map<String, String> params) {    logger.info("此处可以预留资源,或者利用tcc的特点,与AT混用,二阶段时利用一阶段在此处存放的消息,通过二阶段发出,比如redis,mq等操作");}public void commit(BusinessActionContext context) {    logger.info("预留资源真正处理,或者发出mq消息和redis入库");}public void rollback(BusinessActionContext context) {    logger.info("预留资源释放,或清除一阶段准备让二阶段提交时发出的消息缓存");}

🍀业务使用——切点表达式

全局事务:

    @Bean    public AspectTransactionalInterceptor aspectTransactionalInterceptor () {        return new AspectTransactionalInterceptor();    }    @Bean    public Advisor txAdviceAdvisor(AspectTransactionalInterceptor aspectTransactionalInterceptor ) {        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();        pointcut.setExpression("配置切点表达式使全局事务拦截器生效");        return new DefaultPointcutAdvisor(pointcut, aspectTransactionalInterceptor);    }

后记

在这里插入图片描述

👉Java全栈学习路线可参考:【Java全栈学习路线】最全的Java学习路线及知识清单,Java自学方向指引,内含最全Java全栈学习技术清单~
👉算法刷题路线可参考:算法刷题路线总结与相关资料分享,内含最详尽的算法刷题路线指南及相关资料分享~

来源地址:https://blog.csdn.net/qq_42146402/article/details/129819709

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯