当前位置: 首页 > news >正文

阶段八:服务框架高级(第二章:分布式事务)

阶段八:服务框架高级(第二章:分布式事务)

  • Day-分布式事务
  • 0.学习目标
  • 1.分布式事务问题
    • 1.1.本地事务
    • 1.2.分布式事务
    • 1.3.演示分布式事务问题
  • 2.理论基础
    • 2.1.CAP定理
      • 2.1.1.一致性
      • 2.1.2.可用性
      • 2.1.3.分区容错
      • 2.1.4.矛盾
    • 2.2.BASE理论
    • 2.3.解决分布式事务的思路
  • 3.初识`Seata`
    • 3.1.Seata的架构 【==理解==】
    • 3.2.部署TC服务 【==重要,不需要背过,参照文档就可以==】
    • 3.3.微服务集成`Seata` 【==重要==】
      • 3.3.1.引入依赖
      • 3.3.2.配置TC地址
      • 3.3.3.其它服务
      • 3.3.4.小结
  • 4.动手实践
    • 4.1.XA模式 【==重要,用的少==】
      • 4.1.1.两阶段提交
      • 4.1.2.Seata的XA模型 【==理解即可==】
      • 4.1.3.优缺点
      • 4.1.4.实现`XA`模式 【==重要==】
    • 4.2.AT模式 【==重要,重要==】
      • 4.2.1.Seata的AT模型 【==理解==】
      • 4.2.2.流程梳理
      • 4.2.3.AT与XA的区别
      • 4.2.4.脏写问题 【==理解==】
      • 4.2.5.优缺点
      • 4.2.6.实现AT模式 【==重要==】
    • 4.3.TCC模式 【==重要,重要==】
      • 4.3.1.流程分析 【==理解==】
      • 4.3.2.Seata的TCC模型 【==理解==】
      • 4.3.3.优缺点
      • 4.3.4.事务悬挂和空回滚 【==重要==】
        • 1)空回滚
        • 2)业务悬挂
      • 4.3.5.实现TCC模式 【==重要==】
        • 1)思路分析
        • 2)声明TCC接口
        • 2)编写实体类
        • 2)编写`mapper`操作数据表
        • 3)编写实现类 (不全,需要看代码补全)
    • 4.4.SAGA模式 【没怎么说】
      • 4.4.1.原理
      • 4.4.2.优缺点
    • 4.5.四种模式对比
  • 5.高可用 【==重要==】
    • 5.1.高可用架构模型
    • 5.2.实现高可用

Day-分布式事务

0.学习目标

在这里插入图片描述

1.分布式事务问题

1.1.本地事务

本地事务,也就是传统的单机事务。在传统数据库事务中,必须要满足四个原则:
在这里插入图片描述

1.2.分布式事务

  分布式事务:在分布式系统下,一个业务跨越多个服务或数据源,每个服务都是一个分支事务,要保证所有分支事务最终状态一致,这样的事务就是分布式事务
  分布式事务,就是指不是在单个服务或单个数据库架构下,产生的事务,例如:

  • 跨数据源的分布式事务
  • 跨服务的分布式事务
  • 综合情况

  在数据库水平拆分、服务垂直拆分之后,一个业务操作通常要跨多个数据库、服务才能完成。例如电商行业中比较常见的下单付款案例,包括下面几个行为:

  • 创建新订单
  • 扣减商品库存
  • 从用户账户余额扣除金额

完成上面的操作需要访问三个不同的微服务和三个不同的数据库。
在这里插入图片描述
  订单的创建、库存的扣减、账户扣款在每一个服务和数据库内是一个本地事务,可以保证ACID原则。
  但是当我们把三件事情看做一个"业务",要满足保证“业务”的原子性,要么所有操作全部成功,要么全部失败,不允许出现部分成功部分失败的现象,这就是分布式系统下的事务了。
此时ACID难以满足,这是分布式事务要解决的问题

1.3.演示分布式事务问题

我们通过一个案例来演示分布式事务的问题:

1)创建数据库,名为seata_demo,然后导入课前资料提供的SQL文件:
在这里插入图片描述
2)导入课前资料提供的微服务:
在这里插入图片描述
微服务结构如下:
在这里插入图片描述
其中:

seata-demo:父工程,负责管理项目依赖

  • account-service:账户服务,负责管理用户的资金账户。提供扣减余额的接口
  • storage-service:库存服务,负责管理商品库存。提供扣减库存的接口
  • order-service:订单服务,负责管理订单。创建订单时,需要调用account-servicestorage-service

3)启动nacos、所有微服务

4)测试下单功能,发出Post请求:
请求如下:

curl --location --request POST 'http://localhost:8082/order?userId=user202103032042012&commodityCode=100202003032041&count=20&money=200'

如图:
在这里插入图片描述
测试发现,当库存不足时,如果余额已经扣减,并不会回滚,出现了分布式事务问题。
在这里插入图片描述

2.理论基础

解决分布式事务问题,需要一些分布式系统的基础知识作为理论指导。

2.1.CAP定理

1998年,加州大学的计算机科学家 Eric Brewer 提出,分布式系统有三个指标。

  • Consistency(一致性)
  • Availability(可用性)
  • Partition tolerance (分区容错性)

在这里插入图片描述
它们的第一个字母分别是 C、A、P。
  Eric Brewer 说,这三个指标不可能同时做到。这个结论就叫做 CAP 定理。

2.1.1.一致性

Consistency(一致性):用户访问分布式系统中的任意节点,得到的数据必须一致。

比如现在包含两个节点,其中的初始数据是一致的:
在这里插入图片描述
当我们修改其中一个节点的数据时,两者的数据产生了差异:
在这里插入图片描述
要想保住一致性,就必须实现node01node02的数据 同步:
在这里插入图片描述

2.1.2.可用性

Availability (可用性):用户访问集群中的任意健康节点,必须能得到响应,而不是超时或拒绝

如图,有三个节点的集群,访问任何一个都可以及时得到响应:
在这里插入图片描述
当有部分节点因为网络故障或其它原因无法访问时,代表节点不可用:
在这里插入图片描述

2.1.3.分区容错

Partition(分区:因为网络故障或其它原因导致分布式系统中的部分节点与其它节点失去连接,形成独立分区
在这里插入图片描述
Tolerance(容错):在集群出现分区时,整个系统也要持续对外提供服务

2.1.4.矛盾

  在分布式系统中,系统间的网络不能100%保证健康,一定会有故障的时候,而服务有必须对外保证服务。因此Partition Tolerance【分区容错】不可避免。

当节点接收到新的数据变更时,就会出现问题了
在这里插入图片描述
  如果此时要保证一致性,就必须等待网络恢复,完成数据同步后,整个集群才对外提供服务,服务处于阻塞状态,不可用。
  如果此时要保证可用性,就不能等待网络恢复,那node01、node02与node03之间就会出现数据不一致。

也就是说,在P一定会出现的情况下,A和C之间只能实现一个

在这里插入图片描述

2.2.BASE理论

BASE理论是对CAP的一种解决思路,包含三个思想:

  • Basically Available (基本可用):分布式系统在出现故障时,允许损失部分可用性,即保证核心可用。
  • Soft State(软状态): 在一定时间内,允许出现中间状态,比如临时的不一致状态。
  • Eventually Consistent(最终一致性):虽然无法保证强一致性,但是在软状态结束后,最终达到数据一致。

2.3.解决分布式事务的思路

  分布式事务最大的问题是各个子事务的一致性问题,因此可以借鉴CAP定理和BASE理论,有两种解决思路:

  • AP模式各子事务分别执行和提交,允许出现结果不一致,然后采用弥补措施恢复数据即可,实现最终一致

  • CP模式各个子事务执行后互相等待,同时提交,同时回滚,达成强一致。但事务等待过程中,处于弱可用状态

  但不管是哪一种模式,都需要在子系统事务之间互相通讯,协调事务状态,也就是需要一个 事务协调者(TC)
在这里插入图片描述
这里的子系统事务,称为分支事务;有关联的各个分支事务在一起称为全局事务

在这里插入图片描述

3.初识Seata

  Seata是 2019 年 1 月份蚂蚁金服和阿里巴巴共同开源的分布式事务解决方案。致力于提供高性能和简单易用的分布式事务服务,为用户打造一站式的分布式解决方案。

官网地址:http://seata.io/,其中的文档、播客中提供了大量的使用说明、源码分析。
在这里插入图片描述

3.1.Seata的架构 【理解

Seata事务管理中有三个重要的角色:

  • TC (Transaction Coordinator) - 事务协调者: 维护全局和分支事务的状态,协调全局事务提交或回滚

  • TM (Transaction Manager) - 事务管理器: 定义全局事务的范围、开始全局事务、提交或回滚全局事务

  • RM (Resource Manager) - 资源管理器管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚

整体的架构如图:
在这里插入图片描述
Seata基于上述架构提供了四种不同的分布式事务解决方案:

  • XA模式:强一致性分阶段事务模式,牺牲了一定的可用性,无业务侵入
  • TCC模式:最终一致的分阶段事务模式,有业务侵入
  • AT模式:最终一致的分阶段事务模式,无业务侵入,也是Seata的默认模式
  • SAGA模式:长事务模式,有业务侵入

无论哪种方案,都离不开TC,也就是事务的协调者

3.2.部署TC服务 【重要,不需要背过,参照文档就可以

参考课前资料提供的文档《 seata的部署和集成.md 》:
https://editor.csdn.net/md?articleId=128410730
在这里插入图片描述

3.3.微服务集成Seata重要

我们以order-service为例来演示。
实际中每个微服务都要集成一遍Seata,参与全局事务的每个微服务都要做这个动作;

3.3.1.引入依赖

首先,在order-service中引入依赖:

<!--seata-->
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-seata</artifactId><exclusions> <!--代表排除,也要写--><!--版本较低,1.3.0,因此排除--> <exclusion><artifactId>seata-spring-boot-starter</artifactId><groupId>io.seata</groupId></exclusion></exclusions>
</dependency>
<dependency><groupId>io.seata</groupId><artifactId>seata-spring-boot-starter</artifactId><!--seata starter 采用1.4.2版本--><version>${seata.version}</version>  <!--在父工程中有定义版本,也可以直接写版本号-->
</dependency>

3.3.2.配置TC地址

  在order-service中的application.yml中,配置TC服务信息,通过注册中心nacos,结合服务名称获取TC地址:
  下面的是要根据自己的实际来更改的

      group: DEFAULT_GROUP # 分组,默认是DEFAULT_GROUP  ,与nacos浏览器显示的是一样的application: seata-tc-server # seata服务名称  ,与部署TC服务时是一样的seata-demo: SH  #将seata-demo映射到SH集群
seata:registry: # TC服务注册中心的配置,微服务根据这些信息去注册中心获取tc服务地址# 参考tc服务自己的registry.conf中的配置;# 包括:地址、namespace、group、application-name 、clustertype: nacos # 注册中心类型 nacosnacos:  # tcserver-addr: 127.0.0.1:8848 # nacos地址namespace: "" # namespace,默认为空,空就是publicgroup: DEFAULT_GROUP # 分组,默认是DEFAULT_GROUP  ,与nacos浏览器显示的是一样的application: seata-tc-server # seata服务名称  ,与部署TC服务时是一样的username: nacospassword: nacostx-service-group: seata-demo # 事务组名称;根据这个获取tc服务的cluster(集群)名称service:vgroup-mapping: # 事务组与cluster的映射关系seata-demo: SH  #将seata-demo映射到SH集群

在这里插入图片描述
微服务如何根据这些配置寻找TC的地址呢?

我们知道注册到Nacos中的微服务,确定一个具体实例需要四个信息:

  • namespace:命名空间
  • group:分组
  • application:服务名
  • cluster:集群名

以上四个信息,在刚才的yaml文件中都能找到:
在这里插入图片描述
namespace为空,就是默认的public

结合起来,TC服务的信息就是:public@DEFAULT_GROUP@seata-tc-server@SH,这样就能确定TC服务集群了。然后就可以去Nacos拉取对应的实例信息了。

3.3.3.其它服务

  其它两个微服务也都参考order-service的步骤来做,完全一样。

3.3.4.小结

在这里插入图片描述

4.动手实践

下面我们就一起学习下Seata中的四种不同的事务模式。

4.1.XA模式 【重要,用的少

  XA 规范 是 X/Open 组织定义的分布式事务处理(DTP,Distributed Transaction Processing)标准,XA 规范 描述了全局的TM与局部的RM之间的接口几乎所有主流的数据库都对 XA 规范 提供了支持

4.1.1.两阶段提交

  XA是规范,目前主流数据库都实现了这种规范,实现的原理都是基于两阶段提交

正常情况:
在这里插入图片描述
异常情况:
在这里插入图片描述
一阶段:

  • 事务协调者通知每个事物参与者执行本地事务
  • 本地事务执行完成后报告事务执行状态给事务协调者,此时事务不提交,继续持有数据库锁

二阶段:

  • 事务协调者基于一阶段的报告来判断下一步操作
    • 如果一阶段都成功,则通知所有事务参与者,提交事务
    • 如果一阶段任意一个参与者失败,则通知所有事务参与者回滚事务

4.1.2.Seata的XA模型 【理解即可

  Seata对原始的XA模式做了简单的封装和改造,以适应自己的事务模型,基本架构如图:
在这里插入图片描述
RM一阶段的工作:
​ ① 注册分支事务到TC
​ ② 执行分支业务sql但不提交
​ ③ 报告执行状态到TC

TC二阶段的工作:

  • TC检测各分支事务执行状态
    a.如果都成功,通知所有RM提交事务
    b.如果有失败,通知所有RM回滚事务

RM二阶段的工作:

  • 接收TC指令,提交或回滚事务

4.1.3.优缺点

XA模式的优点是什么?

  • 事务的强一致性,满足ACID原则。
  • 常用数据库都支持,实现简单,并且没有代码侵入

XA模式的缺点是什么?

  • 因为一阶段需要锁定数据库资源,等待二阶段结束才释放,性能较差(低可用性)
  • 依赖关系型数据库实现事务(其他数据库有事就不能用了)

4.1.4.实现XA模式 【重要

Seatastarter已经完成了XA模式的自动装配,实现非常简单,步骤如下:

1)修改application.yml文件(每个参与事务的微服务),开启XA模式:

seata:data-source-proxy-mode: XA

2)给发起全局事务的入口方法添加@GlobalTransactional注解:
本例中是OrderServiceImpl中的create方法.
在这里插入图片描述
3)重启服务并测试
重启order-service等每个微服务,再次测试,发现无论怎样,三个微服务都能成功回滚。

4.2.AT模式 【重要,重要

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

4.2.1.Seata的AT模型 【理解

基本流程图:
在这里插入图片描述
阶段一RM的工作:

  • 注册分支事务
  • 记录undo-log(数据快照)
  • 执行业务sql并提交
  • 报告事务状态

阶段二提交时RM的工作:

  • 删除undo-log即可

阶段二回滚时RM的工作:

  • 根据undo-log恢复数据到更新前

4.2.2.流程梳理

我们用一个真实的业务来梳理下AT模式的原理。
比如,现在又一个数据库表,记录用户余额:

idmoney
1100

其中一个分支业务要执行的SQL为:

update tb_account set money = money - 10 where id = 1

AT模式下,当前分支事务执行流程如下:
一阶段:

1)TM发起并注册全局事务到TC
2)TM调用分支事务
3)分支事务准备执行业务SQL
4)RM拦截业务SQL,根据where条件查询原始数据,形成快照。

{"id": 1, "money": 100
}

5)RM执行业务SQL,提交本地事务,释放数据库锁。此时 money = 90
6)RM报告本地事务状态给TC

二阶段:

1)TM通知TC事务结束
2)TC检查分支事务状态
​ a)如果都成功,则立即删除快照
​ b)如果有分支事务失败,需要回滚。读取快照数据({"id": 1, "money": 100}),将快照恢复到数据库。此时数据库再次恢复为100

流程图:
在这里插入图片描述

4.2.3.AT与XA的区别

简述AT模式与XA模式最大的区别是什么?

  • XA模式一阶段不提交事务,锁定资源;AT模式一阶段直接提交,不锁定资源。
  • XA模式依赖数据库机制实现回滚;AT模式利用数据快照实现数据回滚。
  • XA模式强一致;AT模式最终一致

4.2.4.脏写问题 【理解

在多线程并发访问AT模式的分布式事务时,有可能出现脏写问题,如图:
在这里插入图片描述
  解决思路就是引入了全局锁的概念。在释放DB锁(数据库锁)之前,先拿到全局锁。避免同一时刻有另外一个事务来操作当前数据。
在这里插入图片描述
实际还可能存在见缝插针的问题:
解决办法:使用了两个快照更新前的快照、更新后的快照,如果出现问题就会申请人工介入;
在这里插入图片描述

4.2.5.优缺点

AT模式的优点:

  • 一阶段完成直接提交事务,释放数据库资源,性能比较好
  • 利用全局锁实现读写隔离
  • 没有代码侵入,框架自动完成回滚和提交

AT模式的缺点:

  • 两阶段之间属于软状态,属于最终一致
  • 框架的快照功能会影响性能,但比XA模式要好很多

4.2.6.实现AT模式 【重要

  AT模式中的快照生成、回滚等动作都是由框架自动完成,没有任何代码侵入,因此实现非常简单
  只不过,AT模式需要一个表来记录全局锁另一张表来记录数据快照undo_log
  【需要两张表分别记录全局锁数据快照,这两张表分别在不同的数据库中】

1)导入数据库表,记录全局锁
  导入课前资料提供的Sql文件:seata-at.sql,其中 lock_table(全局锁)导入到TC服务关联的数据库seataundo_log(快照)表导入到微服务关联的数据库seata_demo
在这里插入图片描述

DROP TABLE IF EXISTS `lock_table`;
CREATE TABLE `lock_table`  (`row_key` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,`xid` varchar(96) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`transaction_id` bigint(20) NULL DEFAULT NULL,`branch_id` bigint(20) NOT NULL,`resource_id` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`table_name` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`pk` varchar(36) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`gmt_create` datetime NULL DEFAULT NULL,`gmt_modified` datetime NULL DEFAULT NULL,PRIMARY KEY (`row_key`) USING BTREE,INDEX `idx_branch_id`(`branch_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
DROP TABLE IF EXISTS `undo_log`;
CREATE TABLE `undo_log`  (`branch_id` bigint(20) NOT NULL COMMENT 'branch transaction id',`xid` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'global transaction id',`context` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci 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 INDEX `ux_undo_log`(`xid`, `branch_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = 'AT transaction mode undo table' ROW_FORMAT = Compact;

2)修改application.yml文件(每个参与事务的微服务),将事务模式修改为AT模式即可:

seata:data-source-proxy-mode: AT # 默认就是AT

3)重启所有有关的微服务并测试
重启order-service等每个微服务,再次测试,发现无论怎样,三个微服务都能成功回滚。

4.3.TCC模式 【重要,重要

  注意: 并不是所有的微服务都适合使用TCC模式,可以一部分使用AT一部分使用TCC;AT和TCC可以混用;

TCC模式与AT模式非常相似,每阶段都是独立事务,不同的是TCC通过人工编码来实现数据恢复。需要实现三个方法

  • Try:资源的检测和预留;
  • Confirm:完成资源操作业务;要求 Try 成功 Confirm 一定要能成功。
  • Cancel:预留资源释放,可以理解为try的反向操作。

4.3.1.流程分析 【理解

举例,一个扣减用户余额的业务。假设账户A原来余额是100,需要余额扣减30元。

  • 阶段一( Try ):检查余额是否充足,如果充足则冻结金额增加30元,可用余额扣除30
    初识余额:
    在这里插入图片描述
    余额充足,可以冻结:
    在这里插入图片描述
    此时,总金额 = 冻结金额 + 可用金额,数量依然是100不变。事务直接提交无需等待其它事务。

  • 阶段二(Confirm):假如要提交(Confirm),则冻结金额扣减30
    确认可以提交,不过之前可用金额已经扣减过了,这里只要清除冻结金额就好啦:
    在这里插入图片描述
    此时,总金额 = 冻结金额 + 可用金额 = 0 + 70 = 70元

  • 阶段二(Canncel):如果要回滚(Cancel),则冻结金额扣减30,可用余额增加30
    需要回滚,那么就要释放冻结金额,恢复可用金额:
    在这里插入图片描述

4.3.2.Seata的TCC模型 【理解

Seata 中的 TCC模型 依然延续之前的事务架构,如图:
在这里插入图片描述

4.3.3.优缺点

TCC模式的每个阶段是做什么的?

  • Try:资源检查和预留
  • Confirm:业务执行和提交
  • Cancel:预留资源的释放

TCC的优点是什么?

  • 一阶段完成直接提交事务,释放数据库资源,性能好
  • 相比AT模型,无需生成快照,无需使用全局锁,性能最强
  • 不依赖数据库事务,而是依赖补偿操作,可以用于非事务型数据库

TCC的缺点是什么?【消耗人】

  • 有代码侵入,需要人为编写try、Confirm和Cancel接口,太麻烦
  • 软状态,事务是最终一致
  • 需要考虑Confirm和Cancel的失败情况,做好幂等处理

4.3.4.事务悬挂和空回滚 【重要

1)空回滚

  当某分支事务的try阶段阻塞时,可能导致全局事务超时而触发二阶段的cancel操作。在未执行try操作时先执行了cancel操作,这时cancel不能做回滚,就是空回滚
如图:
在这里插入图片描述
执行cancel操作时,应当判断try是否已经执行,如果尚未执行,则应该空回滚。

2)业务悬挂

  对于已经空回滚的业务,之前被阻塞的try操作恢复(但是它恢复的太迟了,整个全局事务已经结束了),继续执行try,就永远不可能confirm或cancel ,事务一直处于中间状态,这就是业务悬挂
  执行try操作时,应当判断cancel是否已经执行过了,如果已经执行,应当阻止空回滚后的try操作,避免悬挂

4.3.5.实现TCC模式 【重要

  注意: 并不是所有的微服务都适合使用TCC模式,可以一部分使用AT一部分使用TCC;AT和TCC可以混用;

  解决空回滚和业务悬挂问题,必须要记录当前事务状态,是在try、还是cancel

1)思路分析

为了实现空回滚、防止业务悬挂,以及幂等性要求。我们必须在数据库记录冻结金额的同时,记录当前事务id和执行状态,为此我们设计了一张表:
在微服务的数据库seata_demo中执行创建

CREATE TABLE `account_freeze_tbl` (`xid` varchar(128) NOT NULL,`user_id` varchar(255) DEFAULT NULL COMMENT '用户id',`freeze_money` int(11) unsigned DEFAULT '0' COMMENT '冻结金额',`state` int(1) DEFAULT NULL COMMENT '事务状态,0:try,1:confirm,2:cancel',PRIMARY KEY (`xid`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT;

其中:

  • xid:是全局事务id
  • freeze_money:用来记录用户冻结金额
  • state:用来记录事务状态

那此时,我们的业务该怎么做呢?

  • Try业务
    • 记录冻结金额和事务状态到account_freeze
    • 扣减account表可用金额
  • Confirm业务
    • 根据xid删除account_freeze表的冻结记录
  • Cancel业务
    • 修改account_freeze表,冻结金额为0,state为2
    • 修改account表,恢复可用金额
  • 如何判断是否空回滚?
    • cancel业务中,根据xid查询account_freeze_tbl表,如果为null则说明try还没做,需要空回滚
  • 如何避免业务悬挂?
    • try业务中,根据xid查询account_freeze_tbl表 ,如果已经存在则证明Cancel已经执行,拒绝执行try业务

接下来,我们改造account-service,利用TCC实现余额扣减功能。

2)声明TCC接口

TCC的Try、Confirm、Cancel方法都需要在接口中基于注解来声明,

我们在account-service账户微服务)项目中的cn.itcast.account.service包中新建一个接口,声明TCC三个接口:

package cn.itcast.account.service;@LocalTCC
public interface AccountTCCService {@TwoPhaseBusinessAction(name = "deduct", commitMethod = "confirm", rollbackMethod = "cancel")//给参数加注解(在deduct方法中@BusinessActionContextParameter)void deduct(@BusinessActionContextParameter(paramName = "userId") String userId,@BusinessActionContextParameter(paramName = "money")int money);boolean confirm(BusinessActionContext ctx); //BusinessActionContext ct上下文对象,可以获取当前的事务信息和参数信息,前提是要给参数加注解(在deduct方法中@BusinessActionContextParameter)boolean cancel(BusinessActionContext ctx);//BusinessActionContext ct上下文对象,可以获取当前的事务信息和参数信息
}

2)编写实体类

账户实体类:

在这里插入图片描述
记录当前事务id和执行状态的实体类,对应上面创建的account_freeze_tbl数据表

在这里插入图片描述

2)编写mapper操作数据表

操作记录当前事务id和执行状态数据表,对应上面创建的account_freeze_tbl数据表:
在这里插入图片描述

操作账户数据表:
在这里插入图片描述

3)编写实现类 (不全,需要看代码补全)

account-service服务中的cn.itcast.account.service.impl包下新建一个类,实现TCC业务:

package cn.itcast.account.service.impl;@Service
@Slf4j
public class AccountTCCServiceImpl implements AccountTCCService {@Autowiredprivate AccountMapper accountMapper;//下面Try方法要操作两张表,把两张表的mapper层的方法注入进来@Autowiredprivate AccountFreezeMapper freezeMapper; //下面Try方法要操作两张表,把两张表的mapper层的方法注入进来@Override@Transactional //下面一抛异常,走到这里回滚public void deduct(String userId, int money) {  //Try方法// 0.获取事务idString xid = RootContext.getXID(); //拿到全局事务id// 1.扣减可用余额accountMapper.deduct(userId, money); //调用mapper层的操作数据库方法// 2.记录冻结金额,事务状态AccountFreeze freeze = new AccountFreeze();  //AccountFreeze的实体类;freeze.setUserId(userId);freeze.setFreezeMoney(money);freeze.setState(AccountFreeze.State.TRY);freeze.setXid(xid);  //使用 // 0.获取事务idfreezeMapper.insert(freeze); //调用mapper层的操作数据库方法}@Overridepublic boolean confirm(BusinessActionContext ctx) {// 1.获取事务idString xid = ctx.getXid();// 2.根据id删除冻结记录int count = freezeMapper.deleteById(xid); //调用mapper层的操作数据库方法return count == 1;}@Overridepublic boolean cancel(BusinessActionContext ctx) {// 0.查询冻结记录String xid = ctx.getXid();AccountFreeze freeze = freezeMapper.selectById(xid); //得到freeze对象// 1.恢复可用余额//参数是用户id和金额accountMapper.refund(freeze.getUserId(), freeze.getFreezeMoney());// 2.将冻结金额清零,状态改为CANCELfreeze.setFreezeMoney(0);  //冻结金额清零freeze.setState(AccountFreeze.State.CANCEL);  //修改状态为CANCELint count = freezeMapper.updateById(freeze); //调用mapper层的操作数据库方法return count == 1;}
}

4.4.SAGA模式 【没怎么说】

Saga 模式是 Seata 即将开源的长事务解决方案,将由蚂蚁金服主要贡献。

其理论基础是Hector & Kenneth 在1987年发表的论文Sagas。

Seata官网对于Saga的指南:https://seata.io/zh-cn/docs/user/saga.html

4.4.1.原理

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

分布式事务执行过程中,依次执行各参与者的正向操作,如果所有正向操作均执行成功,那么分布式事务提交。如果任何一个正向操作执行失败,那么分布式事务会去退回去执行前面各参与者的逆向回滚操作,回滚已提交的参与者,使分布式事务回到初始状态。
在这里插入图片描述
Saga也分为两个阶段:

  • 一阶段:直接提交本地事务
  • 二阶段:成功则什么都不做;失败则通过编写补偿业务来回滚

4.4.2.优缺点

优点:

  • 事务参与者可以基于事件驱动实现异步调用,吞吐高
  • 一阶段直接提交事务,无锁,性能好
  • 不用编写TCC中的三个阶段,实现简单

缺点:

  • 软状态持续时间不确定,时效性差
  • 没有锁,没有事务隔离,会有脏写

4.5.四种模式对比

我们从以下几个方面来对比四种实现:

  • 一致性:能否保证事务的一致性?强一致还是最终一致?
  • 隔离性:事务之间的隔离性如何?
  • 代码侵入:是否需要对业务代码改造?
  • 性能:有无性能损耗?
  • 场景:常见的业务场景

如图:
在这里插入图片描述

5.高可用 【重要

SeataTC服务作为分布式事务核心,一定要保证集群的高可用性。

5.1.高可用架构模型

搭建TC服务集群非常简单,启动多个TC服务,注册到nacos即可。

  但集群并不能确保100%安全,万一集群所在机房故障怎么办?所以如果要求较高,一般都会做异地多机房容灾

比如一个TC集群在上海,另一个TC集群在杭州:
在这里插入图片描述
  微服务基于事务组(tx-service-group)与TC集群的映射关系,来查找当前应该使用哪个TC集群。当SH集群故障时,只需要将vgroup-mapping中的映射关系改成HZ。则所有微服务就会切换到HZ的TC集群了。

5.2.实现高可用

具体实现请参考课前资料提供的文档《seata的部署和集成.md》:
在这里插入图片描述

第三章节:
在这里插入图片描述
直接参考https://editor.csdn.net/md?articleId=128410730即可
【# 叁、TC服务的高可用和异地容灾】

相关文章:

阶段八:服务框架高级(第二章:分布式事务)

阶段八&#xff1a;服务框架高级&#xff08;第二章&#xff1a;分布式事务&#xff09;Day-分布式事务0.学习目标1.分布式事务问题1.1.本地事务1.2.分布式事务1.3.演示分布式事务问题2.理论基础2.1.CAP定理2.1.1.一致性2.1.2.可用性2.1.3.分区容错2.1.4.矛盾2.2.BASE理论2.3.解…...

RPC异步化原理

深入RPC&#xff0c;更好使用RPC&#xff0c;须从RPC框架整体性能考虑问题。得知道如何提升RPC框架的性能、稳定性、安全性、吞吐量及如何在分布式下快速定位问题。RPC框架如何压榨单机吞吐量&#xff1f; 1 前言 TPS一直上不去&#xff0c;压测时CPU压到40%&#xff5e;50%就…...

C# 多窗口切换的实现

1、目的在主窗口中根据不同的按钮选择不同的子窗口显示。2、实现&#xff08;1&#xff09;、创建Winform窗体程序&#xff0c;放入SplitContainer控件splitContainer1将窗体分成左右2部分&#xff1b;&#xff08;2&#xff09;、在左侧splitContainer1.panel1中放入3个Button…...

【深度学习】RNN

1. 什么是RNN 循环神经网络&#xff08;Recurrent Neural Network, RNN&#xff09;是一类以序列&#xff08;sequence&#xff09;数据为输入&#xff0c;在序列的演进方向进行递归&#xff08;recursion&#xff09;且所有节点&#xff08;循环单元&#xff09;按链式连接的递…...

招聘岗位,机会难得

岗位需求 费话不多说&#xff0c;直接上JD&#xff1a; 嵌入式开发工程师&#xff1a; 17:411.计算机、通信等相关专业。 2.熟悉网络基础知识&#xff0c;熟悉802.11a/b/g/n/ac协议&#xff0c;能通过抓包等分析手段排查定位各种wifi相关问题。 3.熟悉路由器主要功能及实现原…...

web打印的几种方法(2023)

在工作中出现web打印的情况是非常多的&#xff0c;其实这也是一个比较烦人的问题&#xff0c;这篇博客整理一下关于Web打印的一些方法或者方式。 1. window.print() 这个方法是用来打印网页的&#xff0c;页面上的其他的元素也会被打印处理&#xff0c;在打印的时候页眉页脚是…...

代码随想录算法训练营day44 | 动态规划之完全背包 518. 零钱兑换 II 377. 组合总和 Ⅳ

day44完全背包基础知识问题描述举个栗子518. 零钱兑换 II1.确定dp数组以及下标的含义2.确定递推公式3.dp数组如何初始化4.确定遍历顺序5.举例推导dp数组377. 组合总和 Ⅳ1.确定dp数组以及下标的含义2.确定递推公式3.dp数组如何初始化4.确定遍历顺序5.举例来推导dp数组完全背包基…...

IntelliJ IDEA 实用插件推荐(包含使用教程)

IntelliJ IDEA 实用插件推荐 背景&#xff1a;电脑重装了&#xff0c;重新下载了最新版的IntelliJ IDEA&#xff0c;感觉默认模式有点枯燥&#xff0c;于是决定从网上下载一些实用美观的插件优化自己以后吃饭的工具&#xff0c;现在推荐的都是目前还能用的&#xff08;亲身实践…...

WideDeep模型

google提出的Wide&deep模型&#xff0c;将线性模型与DNN很好的结合起来&#xff0c;在提高模型泛化能力的同时&#xff0c;兼顾模型的记忆性。wide&deep这种将线性模型与DNN的并行连接模式&#xff0c;后来称为推荐领域的经典模式&#xff0c;奠定了后面深度学习模型的…...

nacos集群模式+keepalived搭建高可用服务

实际工作中如果nacos这样的核心服务停掉了或者整个服务器宕机了&#xff0c;那整个系统也就gg了&#xff0c;所以像这样的核心服务我们必须要搞个3个或者3个以上的nacos集群部署&#xff0c;实现高可用&#xff1b; 部署高可用版本之前&#xff0c;首先你要会部署单机版的naco…...

吉利「银河」负重突围

吉利控股集团最新公布的数据显示&#xff0c;2022年&#xff0c;吉利控股集团汽车总销量超230万辆&#xff0c;同比增长4.3%。其中&#xff0c;新能源汽车销量超64万辆&#xff0c;同比增长100.3%。 在中国本土市场&#xff0c;2022年吉利集团旗下品牌乘用车总交付量为135.84万…...

QT之图形视图框架概述——Graphics View Framework

QT之图形视图框架概述——Graphics View Framework1. 概述2. 核心类3. 事件传递4. Graphics View 坐标系统5. 参考1. 概述 Graphics View Framework是子Qt 4.2引入的&#xff0c;用来取代之前版本中的QCanvas。Graphics View Framework提拱了用于大量2D图形项的管理和交互的能…...

【SQL开发实战技巧】系列(二十二):数仓报表场景(上) 从分析函数效率一定快吗聊一聊结果集分页和隔行抽样实现方式

系列文章目录 【SQL开发实战技巧】系列&#xff08;一&#xff09;:关于SQL不得不说的那些事 【SQL开发实战技巧】系列&#xff08;二&#xff09;&#xff1a;简单单表查询 【SQL开发实战技巧】系列&#xff08;三&#xff09;&#xff1a;SQL排序的那些事 【SQL开发实战技巧…...

小米无线AR眼镜探索版细节汇总

在MWC 2023期间&#xff0c;小米正式发布了一款无线AR眼镜&#xff0c;虽然还没看过实机&#xff0c;但XDA提前上手体验&#xff0c;我们从中进行总结。首先我要说的是&#xff0c;小米这款眼镜和高通无线AR眼镜参考设计高度重叠&#xff0c;产品卖点几乎一致&#xff0c;只是增…...

Web3中文|Litra:简洁而优美的NFT流动性协议,能给NFT市场带来什么?

2021年&#xff0c;NFT元年2021年&#xff0c;无疑是 NFT 的“元年”。这一年推特创始人的首条推特被拍出250万美元&#xff0c;加密艺术家Beeple的数字作品“First 5000 Days”在佳士得以6900万美元价格成交&#xff0c;无聊猿最高上涨了1800倍。2021年11月&#xff0c;在Goog…...

SSL证书对虚拟主机的用处有哪些?

虚拟主机是指在同一台服务器上&#xff0c;通过不同的域名或IP地址为多个网站提供服务的一种网络主机。而SSL证书则是一种数字证书&#xff0c;它用于加密网站与用户之间的通信&#xff0c;确保数据传输的安全性和完整性。在虚拟主机上&#xff0c;SSL证书有以下几个用处&#…...

SpringCloud之MQ笔记分享

MQ异步通信 初始MQ 同步通信 优点&#xff1a;时效性较强&#xff0c;可以以及得到结果 Feign就属于同步方式–问题&#xff1a; 耦合问题性能下降&#xff08;中间的等待时间&#xff09;资源浪费级联失败 异步通信 优点 耦合度低性能提升&#xff0c;吞吐量高故障隔离…...

动态规划背包问题

背包问题的分类 拿到背包问题,最重要的是会归类到哪一种背包问题中,常见的考题里主要是01背包和完全背包,leetcode上连多重背包的题目都没有。实际完全背包问题就是01背包的一种。 对一和零这道题,很多人容易把m看成一个背包,n看成另一个背包,从而当做多重背包。然而这…...

OpenCV4.x图像处理实例-张嘴和闭嘴检测

张嘴和闭嘴检测 在活体验证中,张嘴和闭嘴检测也是一个重要的环节。本文将介绍如何通过检测人脸上唇和下唇的关键点,并计算上唇和下唇的关键点的距离来检测当前人脸状态是否处于张嘴或闭嘴。 张嘴和闭嘴检测主要步骤如下: 第一步,安装依赖库 示例中使用到OpenCV和MediaP…...

软考高级系统分析师系列论文之十二:论实时控制系统与企业信息系统集成在工业控制的常规应用

软考高级系统分析师系列论文之十二:论实时控制系统与企业信息系统集成在工业控制的常规应用 一、摘要二、正文三、总结一、摘要 本文通过“工控组态软件”项目的开发,着重讨论实时系统与信息系统的集成。近年来,国内外的组态软件取得了很大的发展,已广泛应用于企业生产。组…...

网络编程(Modbus进阶)

思维导图 Modbus RTU&#xff08;先学一点理论&#xff09; 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议&#xff0c;由 Modicon 公司&#xff08;现施耐德电气&#xff09;于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…...

【入坑系列】TiDB 强制索引在不同库下不生效问题

文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...

DAY 47

三、通道注意力 3.1 通道注意力的定义 # 新增&#xff1a;通道注意力模块&#xff08;SE模块&#xff09; class ChannelAttention(nn.Module):"""通道注意力模块(Squeeze-and-Excitation)"""def __init__(self, in_channels, reduction_rat…...

条件运算符

C中的三目运算符&#xff08;也称条件运算符&#xff0c;英文&#xff1a;ternary operator&#xff09;是一种简洁的条件选择语句&#xff0c;语法如下&#xff1a; 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true&#xff0c;则整个表达式的结果为“表达式1”…...

苍穹外卖--缓存菜品

1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得&#xff0c;如果用户端访问量比较大&#xff0c;数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据&#xff0c;减少数据库查询操作。 缓存逻辑分析&#xff1a; ①每个分类下的菜品保持一份缓存数据…...

【配置 YOLOX 用于按目录分类的图片数据集】

现在的图标点选越来越多&#xff0c;如何一步解决&#xff0c;采用 YOLOX 目标检测模式则可以轻松解决 要在 YOLOX 中使用按目录分类的图片数据集&#xff08;每个目录代表一个类别&#xff0c;目录下是该类别的所有图片&#xff09;&#xff0c;你需要进行以下配置步骤&#x…...

成都鼎讯硬核科技!雷达目标与干扰模拟器,以卓越性能制胜电磁频谱战

在现代战争中&#xff0c;电磁频谱已成为继陆、海、空、天之后的 “第五维战场”&#xff0c;雷达作为电磁频谱领域的关键装备&#xff0c;其干扰与抗干扰能力的较量&#xff0c;直接影响着战争的胜负走向。由成都鼎讯科技匠心打造的雷达目标与干扰模拟器&#xff0c;凭借数字射…...

论文笔记——相干体技术在裂缝预测中的应用研究

目录 相关地震知识补充地震数据的认识地震几何属性 相干体算法定义基本原理第一代相干体技术&#xff1a;基于互相关的相干体技术&#xff08;Correlation&#xff09;第二代相干体技术&#xff1a;基于相似的相干体技术&#xff08;Semblance&#xff09;基于多道相似的相干体…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...

RSS 2025|从说明书学习复杂机器人操作任务:NUS邵林团队提出全新机器人装配技能学习框架Manual2Skill

视觉语言模型&#xff08;Vision-Language Models, VLMs&#xff09;&#xff0c;为真实环境中的机器人操作任务提供了极具潜力的解决方案。 尽管 VLMs 取得了显著进展&#xff0c;机器人仍难以胜任复杂的长时程任务&#xff08;如家具装配&#xff09;&#xff0c;主要受限于人…...