Seata分布式事务【详解分布式事务AT模式、2PC两阶段提交协议、Seata Server(TC)环境搭建,附有示例+代码】

news/2025/2/25 19:29:56

文章目录

  • 六.Seata 分布式事务
    • 6.1 分布式事务介绍
    • 6.2 常见分布式事务解决方案
    • 6.3 2PC两阶段提交协议:
      • Prepare:提交事务请求
      • Commit:执行事务提交
      • 2PC问题
    • 6.4 AT 模式介绍
    • 6.5 Seata是什么?
    • 6.6 Seata快速开始

六.Seata 分布式事务

6.1 分布式事务介绍

传统单体应用场景下,系统的数据保存在一个数据库实例中,通常场景的关系数据库都能自动提供事务保证,并且这种情况下的事务称为本地事务,能保证原子性、一致性、隔离性、持久性(ACID 特性)。

本地事务 @Transational

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

在这里插入图片描述

分布式事务典型场景

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

跨库事务

跨库事务指的是,一个应用某个功能需要操作多个库,不同的库中存储不同的业务数据。一个服务同时操作2个库的情况:

在这里插入图片描述

分库分表

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

在这里插入图片描述

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

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

服务化

在这里插入图片描述

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

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

  1. seata 阿里分布式事务框架
  2. 消息队列
  3. saga
  4. XA

他们有一个共同点,都是“两阶段(2PC)”。“两阶段”是指完成整个分布式事务,划分成两个步骤完成。实际上,这四种常见的分布式事务解决方案,分别对应着分布式事务的四种模式:AT、TCC、Saga、XA;

6.3 2PC两阶段提交协议:

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

顾名思义,分为两个阶段:Prepare预处理阶段 和 Commit提交阶段

Prepare:提交事务请求

基本流程如下图:

在这里插入图片描述

  1. 询问 协调者向所有参与者发送事务请求,询问是否可执行事务操作,然后等待各个参与者的响应。

  2. 执行 各个参与者接收到协调者事务请求后,执行事务操作(例如更新一个关系型数据库表中的记录),并将Undo 和 Redo 信息记录事务日志中。

  3. 响应 如果参与者成功执行了事务并写入 Undo 和 Redo 信息(也就是说,都成功返回yes,则执行修改后的数据提交到bin.log中),则向协调者返回 YES 响应,否则返回 NO 响应。当然,参与者也可能宕机,从而不会返回响应

解释:
redo(重做)是一种持久化的日志,用于记录数据库的修改操作,以便在系统崩溃或断电时恢复到最近的一致状态。

undo(撤销)是一种用于支持事务回滚的日志,用于记录修改操作的反向操作,以便在事务回滚时撤销对数据库的修改

undo.log:存储修改之前的数据
Redo.log:保存修改之后的数据

需要数据回滚是使用undo,提交数据使用redo入库

Commit:执行事务提交

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

正常提交事务

流程如下图:

在这里插入图片描述

\1. commit 请求 协调者向所有参与者发送 Commit 请求。

\2. 事务提交 参与者收到 Commit 请求后,执行事务提交,提交完成后释放事务执行期占用的所有资源。

\3. 反馈结果 参与者执行事务提交后向协调者发送 Ack 响应。

\4. 完成事务 接收到所有参与者的 Ack 响应后,完成事务提交。

中断事务

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

在这里插入图片描述

\1. rollback 请求 协调者向所有参与者发送 Rollback 请求。

\2. 事务回滚 参与者收到 Rollback 后,使用 Prepare 阶段的 Undo 日志执行事务回滚,完成后释放事务执行期占用的所有资源。

\3. 反馈结果 参与者执行事务回滚后向协调者发送 Ack 响应。

\4. 中断事务 接收到所有参与者的 Ack 响应后,完成事务中断

2PC问题

  1. 同步阻塞 参与者在等待协调者的指令时,其实是在等待其他参与者的响应,在此过程中,参与者是无法进行其他操作的,也就是阻塞了其运行。 倘若参与者与协调者之间网络异常导致参与者一直收不到协调者信息,那么会导致参与者一直阻塞下去。

  2. 单点 在 2PC 中,一切请求都来自协调者,所以协调者的地位是至关重要的,如果协调者宕机,那么就会使参与者一直阻塞并一直占用事务资源。如果协调者也是分布式,使用选主方式提供服务,那么在一个协调者挂掉后,可以选取另一个协调者继续后续的服务,可以解决单点问题。但是,新协调者无法知道上一个事务的全部状态信息(例如已等待 Prepare 响应的时长等),所以也无法顺利处理上一个事务。

  3. 数据不一致 Commit 事务过程中 Commit 请求/Rollback 请求可能因为协调者宕机或协调者与参与者网络问题丢失,那么就导致了部分参与者没有收到 Commit/Rollback 请求,而其他参与者则正常收到执行了Commit/Rollback 操作,没有收到请求的参与者则继续阻塞。这时,参与者之间的数据就不再一致了。当参与者执行 Commit/Rollback 后会向协调者发送 Ack,然而协调者不论是否收到所有的参与者的Ack,该事务也不会再有其他补救措施了,协调者能做的也就是等待超时后像事务发起者返回一个“我不确定该事务是否成功”。

  4. 环境可靠性依赖 协调者 Prepare 请求发出后,等待响应,然而如果有参与者宕机或与协调者之间的网络中断,都会导致协调者无法收到所有参与者的响应,那么在 2PC 中,协调者会等待一定时间,然后超时后,会触发事务中断,在这个过程中,协调者和所有其他参与者都是出于阻塞的。这种机制对网络问题常见的现实环境来说太苛刻了

6.4 AT 模式介绍

AT 模式是一种无侵入的分布式事务解决方案。阿里 Seata 框架,实现了该模式。在 AT 模式下,用户只需关注自己的“业务 SQL”,用户的 “业务 SQL” 作为一阶段,Seata 框架会自动生成事务的二阶段提交和回滚操作。

AT 模式是一种无侵入的分布式事务解决方案。阿里 Seata 框架,实现了该模式。在 AT 模式下,用户只需关注自己的“业务 SQL”,用户的 “业务 SQL” 作为一阶段,Seata 框架会自动生成事务的二阶段提交和回滚操作。

一阶段:

Seata 会拦截" 业务SQL",解析 SQL 语义

查询 “业务SQL” 要更新的业务数据,在业务数据被更新前,将其保存成 “before image”

执行 “业务SQL” ,更新业务数据

查询更新后的数据,将其保存成 “after image”

将 before image 和 after image 保存至 Undo Log 表中生成行锁

以上操作全部在一个数据库事务内完成,这样保证了一阶段操作的原子性。

在这里插入图片描述

二阶段(提交):

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

在这里插入图片描述

二阶段(回滚):

  • 首先要校验脏写,对比“数据库当前业务数据”和 “after image”
    • 如果两份数据完全一致就说明没有脏写,可以还原业务数据。
    • 如果不一致就说明有脏写,出现脏写就需要转人工处理。
  • 用“before image”还原业务数据
  • 删除快照数据和行锁

在这里插入图片描述

在这里插入图片描述

6.5 Seata是什么?

Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和XA 事务模式,为用户打造一站式的分布式解决方案。AT模式是阿里首推的模式。

Seata的三大角色

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

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

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

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

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

在这里插入图片描述

1.TM 请求 TC 开启一个全局事务。TC 会生成一个 XID 作为该全局事务的编号。XID,会在微服务的调用链路中传播,保证将多个微服务的子事务关联在一起。 当一进入事务方法中就会生成XID ,global_table 就是存储的全局事务信息 ,

2.RM 请求 TC 将本地事务注册为全局事务的分支事务,通过全局事务的 XID 进行关联。 当运行数据库操作方法,branch_table 存储事务参与者

3.TM 请求 TC 告诉 XID 对应的全局事务是进行提交还是回滚。

4.TC 驱动 RM 们将 XID 对应的自己的本地事务进行提交还是回滚。

在这里插入图片描述

6.6 Seata快速开始

https://seata.io/zh-cn/docs/ops/deploy-guide-beginner.html 官网文档

Seata Server(TC)环境搭建

下载安装包

https://github.com/seata/seata/releases 下载1.3.0版本

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

Server端存储模式(store.mode)支持三种:

  • file:(默认)单机模式,全局事务会话信息内存中读写并持久化本地文件root.data,性能较高(默认)
  • db:(5.7+)高可用模式,全局事务会话信息通过db共享,相应性能差些
  • redis: Seata-Server 1.3及以上版本支持,性能较高,存在事务信息丢失风险,请提前配置适合当前场景的redis持久化配置

使用db模式进行seata-server存储

打开seata/config/file.conf

修改mode=“db”

修改数据库连接信息(URL\USERNAME\PASSWORD)

在这里插入图片描述

在这里插入图片描述

 url = "jdbc:mysql://127.0.0.1:3306/seata_server?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC"

创建数据库seata_server

在这里插入图片描述

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

也可以:

在这里插入图片描述

将mysql.sql文件执行以下,可以得出表

在这里插入图片描述

在这里插入图片描述

Nacos(注册&配置中心)配置

将Seata Server注册到Nacos,并配置到配置中心

打开 seata/conf/registry.conf ,修改conf目录下的registry.conf配置:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

将D:\seata-1.3.0下的整个 script 文件夹,复制到 D:\seata下的bin 同级目录下

在这里插入图片描述

在这里插入图片描述

修改 /seata/script/config-center/config.txt 文件

在这里插入图片描述

在这里插入图片描述

打开D:\seata\script\config-center\nacos 修改nacos-config.sh

在这里插入图片描述

在这里插入图片描述

双击启动nacos-config.sh文件(必须安装git)

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

目的:将seata配置信息加载到Nacos配置中心(太多了,不太好创建)

启动:seata-server

在bin目录下,双击seata-server.bat,默认端口8091

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

搭建分布式事务微服务场景Seata Client

创建两个数据库seata_stock与seata_order

分别在seata_stock下创建stock_tab与seata_order下order_tab

分别在seata_stock下创建stock_tab与seata_order下order_tab

# 库存表
CREATE TABLE `stock_tab` (
`id` int(11) NOT NULL AUTO_INCREMENT, #库存ID
`product_id` int(11) NULL DEFAULT NULL, #商品ID
`count` int(11) NULL DEFAULT 0, #商品数量
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE =
utf8_general_ci ROW_FORMAT = Compact;
CREATE TABLE `undo_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`branch_id` bigint(20) NOT NULL,
`xid` varchar(100) NOT NULL,
`context` varchar(128) NOT NULL,
`rollback_info` longblob NOT NULL,
`log_status` int(11) NOT NULL,
`log_created` datetime NOT NULL,
`log_modified` datetime NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;



# 订单表
CREATE TABLE `order_tab` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`product_id` int(11) NULL DEFAULT 0 COMMENT '商品id',
`total_amount` int(11) NULL DEFAULT 0 COMMENT '总金额',
`status` int(255) NULL DEFAULT NULL COMMENT '0->待付款;1->待发货',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE =
utf8_general_ci ROW_FORMAT = Compact;
CREATE TABLE `undo_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`branch_id` bigint(20) NOT NULL,
`xid` varchar(100) NOT NULL,
`context` varchar(128) NOT NULL,
`rollback_info` longblob NOT NULL,
`log_status` int(11) NOT NULL,
`log_created` datetime NOT NULL,
`log_modified` datetime NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

在这里插入图片描述

在这里插入图片描述

业务场景:

  • 用户下单,整个业务逻辑由三个微服务构成:
  • 订单服务:根据采购需求创建订单。
  • 库存服务:对给定的商品扣除库存数量

运行代码发现:使用本地事务,订单插入失败,库存却成功更新。。。。。。

在这里插入图片描述

AT模式 seata

在客户端项目pom中引入seata

<!-- seata 分布式事务 -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
     <artifactId>
       spring-cloud-starter-alibaba-seata
      </artifactId>
      <version>2.1.3.RELEASE</version>
</dependency>

在service中添加**@GlobalTransactional**注解(TM方)

在这里插入图片描述

运行测试。。。。。。

如果报错:请在添加订单与修改库存的业务中打印xid 查询是否保持一致


http://www.niftyadmin.cn/n/5865883.html

相关文章

Apache Pinpoint工具介绍

Apache Pinpoint&#xff1a;分布式系统性能分析与链路追踪 一、Pinpoint 简介 Apache Pinpoint 是一个开源的 分布式追踪系统&#xff0c;专为微服务架构设计&#xff0c;支持 HTTP、RPC、MQTT 等协议的调用链追踪。其核心功能包括&#xff1a; 链路可视化&#xff1a;展示…

一个用于测试内存屏障差异的 C 语言示例程序

下面是一个用于测试内存屏障差异的 C 语言示例程序,演示在弱内存模型(如 ARM Cortex-A35)中,指令重排序可能导致的数据不一致问题,并对比不同同步机制的效果: 测试目标 验证以下场景: 无内存屏障:多线程环境下可能出现数据竞争。内存屏障:强制指令顺序,避免数据竞争…

香港多IP站群服务器

香港多IP站群服务器是指一种具有多个独立IP地址、能够同时运行多个网站或应用的服务器。这种服务器特别适用于需要大量IP地址来扩展和管理多个站点的企业或个人&#xff0c;尤其是在SEO优化、网络营销、以及需要避免IP封锁或限制的场景下。下面是对香港多IP站群服务器的详细扩展…

如何使用爬虫获取淘宝商品详情:API返回值说明与案例指南

在电商数据分析和运营中&#xff0c;获取淘宝商品详情是常见的需求。淘宝开放平台提供了丰富的API接口&#xff0c;允许开发者通过合法的方式获取商品信息。本文将详细介绍如何使用PHP编写爬虫&#xff0c;通过淘宝API获取商品详情&#xff0c;并解析API返回值的含义和结构。 一…

Java Map实现类面试题

Java Map实现类面试题 HashMap Q1: HashMap的实现原理是什么&#xff1f; HashMap基于哈希表实现&#xff0c;使用数组链表红黑树&#xff08;Java 8&#xff09;的数据结构。 public class HashMapPrincipleExample {// 模拟HashMap的基本结构public class SimpleHashMap&…

分布式深度学习:探索无限可能

分布式深度学习:探索无限可能 大家好,我是Echo_Wish,一名专注于人工智能和Python的自媒体创作者。今天,我们将深入探讨分布式深度学习,这个技术不仅是AI发展的前沿,更是应对大规模数据和复杂模型的关键解决方案。随着数据量和模型复杂度的不断增加,传统的单机深度学习已…

netty十八罗汉之——挖耳罗汉(Decoder)

佛教中除不听各种淫邪声音之外&#xff0c;更不可听别人的秘密。因他论耳根最到家&#xff0c;故取挖耳之形&#xff0c;以示耳根清净。 来看看netty的核心组件解码器Decoder Decoder的作用半包&#xff0c;粘包问题从模板和装饰器模式看Decoder解码原理 1.Decoder作用 最根本…

解决鼠标唤醒关屏状态下的笔记本

以下是通过计划任务和PowerShell实现鼠标唤醒控制的全网独家解决方案,基于Windows事件触发机制,结合设备管理API实现精准控制,最终实现仅需通过win+l锁定屏幕,再关闭屏幕,既不会出现唤醒笔记问的问题: 一、技术原理深度解析 1. 事件触发机制 Windows安全子系统在锁屏/…