微服务——分布式事务

embedded/2024/10/9 7:20:07/

目录

分布式事务

1.1分布式事务的特性

1.2分布式事务应用背景

​编辑

1.3.认识Seata

1.4部署TC服务

1.4.1.准备数据库表

1.4.2.准备配置文件

1.4.3.Docker部署

1.5.微服务集成Seata

1.5.1.引入依赖

1.5.2.改造配置

1.5.3.添加数据库表

​编辑1.6.XA模式

1.6.1.两阶段提交

1.6.2.Seata的XA模型

1.6.3.优缺点

1.6.4.实现步骤

1.7.AT模式

1.7.1Seata的AT模型

1.7.2.流程梳理

1.7.3..AT与XA的区别


分布式事务

        分布式事务是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上的一种事务处理方式。它旨在解决在分布式环境下,多个数据库或资源服务器之间数据一致性的问题。

1.1分布式事务的特性

        分布式事务继承了传统事务的ACID特性,即原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)。这些特性在分布式环境下尤为重要,因为它们确保了事务的完整性和数据的可靠性。

  1. 原子性:事务中的所有操作要么全部成功,要么全部失败回滚,以保证数据的完整性。
  2. 一致性:事务执行前后,数据的完整性必须保持一致。
  3. 隔离性:在并发环境中,一个事务的执行不会被其他事务干扰。
  4. 持久性:事务一旦提交,它对数据库的修改就是永久性的。

1.2分布式事务应用背景

如上图,由于订单、购物车、商品分别在三个不同的微服务,而每个微服务都有自己独立的数据库,因此下单过程中就会跨多个数据库完成业务。而每个微服务都会执行自己的本地事务:

  • 交易服务:下单事务

  • 购物车服务:清理购物车事务

  • 库存服务:扣减库存事务

        整个业务中,各个本地事务是有关联的。因此每个微服务的本地事务,也可以称为分支事务。多个有关联的分支事务一起就组成了全局事务。我们必须保证整个全局事务同时成功或失败。而微服务中的事务并未遵循ACID的原则,归其原因就是参与事务的多个子业务在不同的微服务,跨越了不同的数据库。虽然每个单独的业务都能在本地遵循ACID,但是它们互相之间没有感知,不知道有人失败了,无法保证最终结果的统一,也就无法遵循ACID的事务特性了。

1.3.认识Seata

        解决分布式事务的方案有很多,但实现起来都比较复杂,因此我们一般会使用开源的框架来解决分布式事务问题。在众多的开源分布式事务框架中,功能最完善、使用最多的就是阿里巴巴在2019年开源的Seata了。

Seata 是什么? | Apache Seataicon-default.png?t=O83Ahttps://seata.apache.org/zh-cn/docs/overview/what-is-seata/

        其实分布式事务产生的一个重要原因,就是参与事务的多个分支事务互相无感知,不知道彼此的执行状态。因此解决分布式事务的思想非常简单:

        就是找一个统一的事务协调者,与多个分支事务通信,检测每个分支事务的执行状态,保证全局事务下的每一个分支事务同时成功或失败即可。大多数的分布式事务框架都是基于这个理论来实现的。

Seata也不例外,在Seata的事务管理中有三个重要的角色:

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

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

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

Seata的工作架构如图所示:

        其中,TMRM可以理解为Seata的客户端部分,引入到参与事务的微服务依赖中即可。将来TMRM就会协助微服务,实现本地分支事务与TC之间交互,实现事务的提交或回滚。

TC服务则是事务协调中心,是一个独立的微服务,需要单独部署。

1.4部署TC服务

1.4.1.准备数据库表

        Seata支持多种存储模式,但考虑到持久化的需要,我们一般选择基于数据库存储。

1.4.2.准备配置文件

我们将整个seata文件夹拷贝到虚拟机的/root目录:

1.4.3.Docker部署

需要注意,要确保nacos、mysql都在hm-net网络中。如果某个容器不再hm-net网络,可以参考下面的命令将某容器加入指定网络:

docker network connect [网络名] [容器名]

在虚拟机的/root目录执行下面的命令:

docker run --name seata \
-p 8099:8099 \
-p 7099:7099 \
-e SEATA_IP=192.168.150.101 \
-v ./seata:/seata-server/resources \
--privileged=true \
--network hm-net \
-d \
seataio/seata-server:1.5.2

1.5.微服务集成Seata

1.5.1.引入依赖

为了方便各个微服务集成seata,我们需要把seata配置共享到nacos,因此trade-service模块不仅仅要引入seata依赖,还要引入nacos依赖:

<!--统一配置管理--><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.5.2.改造配置

首先在nacos上添加一个共享的seata配置

内容如下:

seata:registry: # TC服务注册中心的配置,微服务根据这些信息去注册中心获取tc服务地址type: nacos # 注册中心类型 nacosnacos:server-addr: 192.168.150.101:8848 # nacos地址namespace: "" # namespace,默认为空group: DEFAULT_GROUP # 分组,默认是DEFAULT_GROUPapplication: seata-server # seata服务名称username: nacospassword: nacostx-service-group: hmall # 事务组名称service:vgroup-mapping: # 事务组与tc集群的映射关系hmall: "default"

然后,改造微服务模块,添加bootstrap.yaml,内容如下:

spring:application:name: trade-service # 服务名称profiles:active: devcloud:nacos:server-addr: 192.168.150.101 # nacos地址config:file-extension: yaml # 文件后缀名shared-configs: # 共享配置- dataId: shared-jdbc.yaml # 共享mybatis配置- dataId: shared-log.yaml # 共享日志配置- dataId: shared-swagger.yaml # 共享日志配置- dataId: shared-seata.yaml # 共享seata配置

可以看到这里加载了共享的seata配置。然后改造application.yaml文件,内容如下:

server:port: 8085
feign:okhttp:enabled: true # 开启OKHttp连接池支持sentinel:enabled: true # 开启Feign对Sentinel的整合
hm:swagger:title: 交易服务接口文档package: com.hmall.trade.controllerdb:database: hm-trade

1.5.3.添加数据库表

seata的客户端在解决分布式事务的时候需要记录一些中间数据,保存在数据库中。因此我们要先准备一个这样的表。

1.6.XA模式

Seata支持四种不同的分布式事务解决方案:

  • XA

  • TCC

  • AT

  • SAGA

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

1.6.1.两阶段提交

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

正常情况:

异常情况:

一阶段:

  • 事务协调者通知每个事务参与者执行本地事务

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

二阶段:

  • 事务协调者基于一阶段的报告来判断下一步操作

  • 如果一阶段都成功,则通知所有事务参与者,提交事务

  • 如果一阶段任意一个参与者失败,则通知所有事务参与者回滚事务

1.6.2.Seata的XA模型

Seata对原始的XA模式做了简单的封装和改造,以适应自己的事务模型,基本架构如图:

RM一阶段的工作:

  1. 注册分支事务到TC

  2. 执行分支业务sql但不提交

  3. 报告执行状态到TC

TC二阶段的工作:

1.TC检测各分支事务执行状态

  • 如果都成功,通知所有RM提交事务
  • 如果有失败,通知所有RM回滚事务

RM二阶段的工作:

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

1.6.3.优缺点

XA模式的优点是什么?

  • 事务的强一致性,满足ACID原则

  • 常用数据库都支持,实现简单,并且没有代码侵入

XA模式的缺点是什么?

  • 因为一阶段需要锁定数据库资源,等待二阶段结束才释放,性能较差

  • 依赖关系型数据库实现事务

1.6.4.实现步骤

首先,我们要在配置文件中指定要采用的分布式事务模式。我们可以在Nacos中的共享shared-seata.yaml配置文件中设置:

seata:data-source-proxy-mode: XA

其次,我们要利用@GlobalTransactional标记分布式事务的入口方法:

1.7.AT模式

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

1.7.1Seata的AT模型

基本流程图:

阶段一RM的工作:

  • 注册分支事务

  • 记录undo-log(数据快照)

  • 执行业务sql并提交

  • 报告事务状态

阶段二提交时RM的工作:

  • 删除undo-log即可

阶段二回滚时RM的工作:

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

1.7.2.流程梳理

我们用一个真实的业务来梳理下AT模式的原理。

比如,现在有一个数据库表,记录用户余额:

id

money

1

100

其中一个分支业务要执行的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
}
  1. RM执行业务SQL,提交本地事务,释放数据库锁。此时 money = 90

  2. RM报告本地事务状态给TC

二阶段

  1. TM通知TC事务结束

  2. TC检查分支事务状态

    1. 如果都成功,则立即删除快照

    2. 如果有分支事务失败,需要回滚。读取快照数据({"id": 1, "money": 100}),将快照恢复到数据库。此时数据库再次恢复为100

流程图:

1.7.3..AT与XA的区别

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

  • XA模式一阶段不提交事务,锁定资源;AT模式一阶段直接提交,不锁定资源。

  • XA模式依赖数据库机制实现回滚;AT模式利用数据快照实现数据回滚。

  • XA模式强一致;AT模式最终一致

可见,AT模式使用起来更加简单,无业务侵入,性能更好。因此企业90%的分布式事务都可以用AT模式来解决。


http://www.ppmy.cn/embedded/124941.html

相关文章

【Linux】几种常见配置文件介绍

配置文件目录 linux 系统中有很多配置文件目录 /etc/systemd/system /lib/systemd/system /usr/lib/systemd/system 【结果就是这个目录配置文件是源头】 这三者有什么样的关系呢&#xff1f; 以下是网络上找的资料汇总&#xff0c;并加了一些操作验证。方便后期使用 介…

C++ STL中 set 和 map 的区别

目录 1.引言 2.set 3.map 4.共同点 5.不同点 6.高级用法与注意事项 7.总结 1.引言 在C的标准模板库&#xff08;STL&#xff09;中&#xff0c;set和map是两种常用的容器&#xff0c;它们提供了不同的功能和使用场景。尽管它们底层都基于红黑树实现&#xff0c;有许多相…

Collection 和 Collections 有什么区别?

Collection 和 Collections 在 Java 中是两个截然不同的概念&#xff0c;它们之间的主要区别体现在定义、性质、功能和使用上。 一、定义与性质 Collection 定义&#xff1a;Collection 是 Java 集合框架中的一个根接口&#xff0c;表示一组对象的集合。性质&#xff1a;它是一…

Java入门(基础,常见API,JVM,JUC并发编程)

一.javaSE Java初学者软件安装与idea快捷键-CSDN博客 Java基本概念&#xff08;新手入门&#xff09;_阿伟java资料-CSDN博客 二.常见API JavaAPI-CSDN博客 三.JVM JVM入门-CSDN博客 四.JUC并发编程 JUC并发编程-CSDN博客

【SQL】掌握SQL查询技巧:数据筛选与限制

目录 1. DISTINCT&#xff1a;避免重复记录1.1 示意图1.2 使用场景 2. LIMIT&#xff1a;控制查询结果的数量2.1 示意图2.2 使用场景 3. OFFSET&#xff1a;跳过前几行3.1 示意图3.2 使用场景 4. WHERE子句&#xff1a;精细控制数据过滤4.1 示意图4.2 运算符详细说明4.3 基本条…

边缘端大模型是怎么部署的?重点关注哪些?

写在前面 在设备端运行的大语言模型&#xff08;LLMs&#xff09;&#xff0c;即指在边缘设备上运行LLMs&#xff0c;因其出色的隐私保护、低延迟和节省带宽的特点而引起了广泛关注。然而&#xff0c;与功能更为强大的云中心相比&#xff0c;设备端LLMs的能力本质上受到边缘设…

旅游避坑指南

1.火车站旁白的小摊贩&#xff0c;还有周边的小饭店百分之百是黑店&#xff0c;不仅难吃要死而且巨黑&#xff01;&#xff01;&#xff01; 可以地图上搜索附近的大型商超&#xff0c;例如泰安市的银座商超&#xff0c;里面的东西不仅好吃而且价格透明&#xff0c;还有很多当…

Qt 6 相比 Qt 5 的主要提升与更新

自从 Qt 6 发布以来&#xff0c;作为 Qt 框架的一个重大版本更新&#xff0c;它在多个核心方面进行了深度优化和改进。与 Qt 5 相比&#xff0c;Qt 6 不仅提升了性能&#xff0c;还改进了对现代硬件和图形 API 的支持&#xff0c;并增强了开发者的工作流程。本文将详细介绍 Qt …