还不会分布式事务,seata xa模式入门实战送上

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 还不会分布式事务,seata xa模式入门实战送上

前言


目前微服务的火热程度不用多说,简历上不写个熟悉Spring Cloud的框架感觉都没有竞争力。但是玩了这么久的微服务,其中关键的分布式事务问题,你真的掌握了吗?


今天带大家使用seata xa模式轻松解决分布式事务问题。


一、什么是seata?


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


二、seata原理说明


1、角色说明

Seata分TC、TM和RM三个角色,TC(Server端)为单独服务端部署,TM和RM(Client端)由业务系统集成。


TC (Transaction Coordinator) - 事务协调者

维护全局和分支事务的状态,驱动全局事务提交或回滚。


TM (Transaction Manager) - 事务管理器

定义全局事务的范围:开始全局事务、提交或回滚全局事务。


RM (Resource Manager) - 资源管理器

管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。

67.png


2、什么是 Seata 的事务模式?

Seata 定义了全局事务的框架。


全局事务 定义为若干 分支事务 的整体协调:

1、TM 向 TC 请求发起(Begin)、提交(Commit)、回滚(Rollback)全局事务。

2、TM 把代表全局事务的 XID 绑定到分支事务上。

3、RM 向 TC 注册,把分支事务关联到 XID 代表的全局事务中。

4、RM 把分支事务的执行结果上报给 TC。(可选)

5、TC 发送分支提交(Branch Commit)或分支回滚(Branch Rollback)命令给 RM。


Seata 的 全局事务 处理过程,分为两个阶段:

1、执行阶段 :执行 分支事务,并 保证 执行结果满足是 可回滚的(Rollbackable) 和 持久化的(Durable)。

2、完成阶段: 根据 执行阶段 结果形成的决议,应用通过 TM 发出的全局提交或回滚的请求给 TC,TC 命令 RM 驱动 分支事务 进行 Commit 或 Rollback。


Seata 的所谓 事务模式 是指:运行在 Seata 全局事务框架下的 分支事务 的行为模式。准确地讲,应该叫作 分支事务模式。

不同的 事务模式 区别在于 分支事务 使用不同的方式达到全局事务两个阶段的目标。即,回答以下两个问题:

1、执行阶段 :如何执行并 保证 执行结果满足是 可回滚的(Rollbackable) 和 持久化的(Durable)。

2、完成阶段: 收到 TC 的命令后,如何做到分支的提交或回滚?


以我们 Seata 的 AT 模式和 TCC 模式为例来理解:

AT 模式:


执行阶段:

可回滚:根据 SQL 解析结果,记录回滚日志

持久化:回滚日志和业务 SQL 在同一个本地事务中提交到数据库


完成阶段:

分支提交:异步删除回滚日志记录

分支回滚:依据回滚日志进行反向补偿更新


TCC 模式:


执行阶段

调用业务定义的 Try 方法(完全由业务层面保证 可回滚 和 持久化)


完成阶段:

分支提交:调用各事务分支定义的 Confirm 方法

分支回滚:调用各事务分支定义的 Cancel 方法


三、SEATA 的分布式案例


1、业务逻辑说明

用户购买商品的业务逻辑。整个业务逻辑由4个微服务提供支持:

交易服务(Business):购买商品,发起交易,开启全局事务,作为TM

仓储服务(Storage):对给定的商品扣除仓储数量。作为RM

订单服务(Order):根据采购需求创建订单。作为RM

帐户服务(Account):从用户帐户中扣除余额。作为RM

seate server作为TC事务协调者,维护全局和分支事务的状态


2、架构图

66.png


3、SEATA 的分布式交易解决方案

65.png

具体的执行流程如下:

1、交易服务接收到购买请求,作为TM向TC申请开启一个全局事务,全局事务创建成功并生成一个全局唯一的XID。

2、交易服务向库存服务(Storage)和订单服务(Order)发起RPC请求。(XID在微服务调用链路的上下文中传播)

3、库存服务(Storage)和订单服务(Order)向TC注册分支事务,并执行扣减库存,生产订单的业务逻辑,并将其纳入XID对应的全局事务的管辖。

4、订单服务(Order)向账户服务(Account)发起请求扣减账户余额,账户服务(Account)向TC注册分支事务,执行扣减账户余额操作。

5、账户服务(Account),订单服务(Order),库存服务(Storage)分支事务执行完毕。

6、TM向TC发起针对XID的全局提交或回滚决议。

7、TC调度XID下管辖的全部分支事务完成提交或回滚请求。


四、Seata XA模式说明


Seata 1.2.0 版本重磅发布新的事务模式:XA 模式,实现对 XA 协议的支持。


1、什么是 XA?

XA 规范 是 X/Open 组织定义的分布式事务处理(DTP,Distributed Transaction Processing)标准。


XA 规范 描述了全局的事务管理器与局部的资源管理器之间的接口。XA规范 的目的是允许的多个资源(如数据库,应用服务器,消息队列等)在同一事务中访问,这样可以使 ACID 属性跨越应用程序而保持有效。


XA 规范 使用两阶段提交(2PC,Two-Phase Commit)来保证所有资源同时提交或回滚任何特定的事务。


XA 规范 在上世纪 90 年代初就被提出。目前,几乎所有主流的数据库都对 XA 规范 提供了支持。


2、什么是 Seata 的 XA 模式?

XA 模式:

在 Seata 定义的分布式事务框架内,利用事务资源(数据库、消息服务等)对 XA 协议的支持,以 XA 协议的机制来管理分支事务的一种 事务模式。


执行阶段:

可回滚:业务 SQL 操作放在 XA 分支中进行,由资源对 XA 协议的支持来保证 可回滚

持久化:XA 分支完成后,执行 XA prepare,同样,由资源对 XA 协议的支持来保证 持久化(即,之后任何意外都不会造成无法回滚的情况)


完成阶段:

分支提交:执行 XA 分支的 commit

分支回滚:执行 XA 分支的 rollback


3、为什么支持 XA ?

为什么要在 Seata 中增加 XA 模式呢?支持 XA 的意义在哪里呢?

1、补偿型事务模式的问题

本质上,Seata 已经支持的 3 大事务模式:AT、TCC、Saga 都是 补偿型 的。

补偿型 事务处理机制构建在 事务资源 之上(要么在中间件层面,要么在应用层面),事务资源 本身对分布式事务是无感知的。


事务资源 对分布式事务的无感知存在一个根本性的问题:无法做到真正的 全局一致性 。


比如,一条库存记录,处在 补偿型 事务处理过程中,由 100 扣减为 50。此时,仓库管理员连接数据库,查询统计库存,就看到当前的 50。之后,事务因为异外回滚,库存会被补偿回滚为 100。显然,仓库管理员查询统计到的 50 就是 脏 数据。


可以看到,补偿型 分布式事务机制因为不要求 事务资源 本身(如数据库)的机制参与,所以无法保证从事务框架之外的全局视角的数据一致性。


2、XA 的价值

与 补偿型 不同,XA 协议 要求 事务资源 本身提供对规范和协议的支持。


因为 事务资源 感知并参与分布式事务处理过程,所以 事务资源(如数据库)可以保障从任意视角对数据的访问有效隔离,满足全局数据一致性。


比如,上一节提到的库存更新场景,XA 事务处理过程中,中间态数据库存 50 由数据库本身保证,是不会仓库管理员的查询统计 看 到的。(当然隔离级别需要 读已提交 以上)


除了 全局一致性 这个根本性的价值外,支持 XA 还有如下几个方面的好处:

1、业务无侵入:和 AT 一样,XA 模式将是业务无侵入的,不给应用设计和开发带来额外负担。

2、数据库的支持广泛:XA 协议被主流关系型数据库广泛支持,不需要额外的适配即可使用。

3、多语言支持容易:因为不涉及 SQL 解析,XA 模式对 Seata 的 RM 的要求比较少,为不同语言开发 SDK 较之 AT 模式将更 薄,更容易。

4、传统基于 XA 应用的迁移:传统的,基于 XA 协议的应用,迁移到 Seata 平台,使用 XA 模式将更平滑。


4、XA 模式的前提

支持XA 事务的数据库。

Java 应用,通过 JDBC 访问数据库。


5、XA 整体机制

在 Seata 定义的分布式事务框架内,利用事务资源(数据库、消息服务等)对 XA 协议的支持,以 XA 协议的机制来管理分支事务的一种 事务模式。

64.png

执行阶段:

可回滚:业务 SQL 操作放在 XA 分支中进行,由资源对 XA 协议的支持来保证 可回滚

持久化:XA 分支完成后,执行 XA prepare,同样,由资源对 XA 协议的支持来保证 持久化(即,之后任何意外都不会造成无法回滚的情况)


完成阶段:

分支提交:执行 XA 分支的 commit

分支回滚:执行 XA 分支的 rollback


6、XA 工作机制

1. 整体运行机制

XA 模式 运行在 Seata 定义的事务框架内:

63.png

执行阶段(E xecute):

XA start/XA end/XA prepare + SQL + 注册分支


完成阶段(F inish):

XA commit/XA rollback


2. 数据源代理

XA 模式需要 XAConnection。


五、seata server安装


1、下载最新版本的 Seata Sever

这里我下载的是最新版seata-server-1.4.1


2、解压并启动 Seata server

unzip seata-server-1.4.1.zip
cd seate/bin
sh seata-server.sh -p 8091 -h 127.0.0.1 -m file


配置暂时不做任何修改。默认会采用file存储模式。


六、项目实战


可以参考 Seata 官网的样例:seata-xa

不知道大家下载后能否运行,反正我是折腾半天,还是启动失败。最后参考着自己对项目进行重建。


1、执行sql/all_in_one.sql,建立相关业务表

sql存放的地址:all_in_one.sql

62.png


2、构建工程seata-xa

61.png

3、maven依赖

需要注意一下问题:

3.1 Spring boot和Spring Cloud的版本对应关系

60.png

这里我采用的是Spring Boot 2.2.6.RELEASE,Spring Cloud采用的Hoxton.SR10

59.png

3.2 没有采用官网demo中的spring-cloud-alibaba-seata,而是seata-spring-boot-starter 1.4.1

经尝试采用spring-cloud-alibaba-seata时,项目启动一直报错,暂时没有找到解决办法。

seata-spring-boot-starter的版本选用1.4.1,这样才支持DataSourceProxyXA。并且可以和seata-server-1.4.1版本对应上。

58.png


4、添加seate相关属性

seata.tx-service-group=my_test_tx_group
seata.data-source-proxy-mode=XA
seata.registry.type=file


属性说明:

seata.tx-service-group属性是配置seata事务服务的分组名称,需要和seate server的配置保持一直。默认值为my_test_tx_group。

seata.data-source-proxy-mode属性用来指定数据源的代理模式,支持AT和XA,如果选择AT模式的话,需要在数据源中创建undo_log事务回滚日志表。

seata.registry.type属性用来指定seata的注册中心的类型,目前支持file,nacos,eureka,redis,zk等多种模式,默认为file。


5、数据源的配置类都可以省略

seata-spring-boot-starter会自动根据配置seata.data-source-proxy-mode启用不同的数据源代理。

不需要在代码中进行控制。

@Bean("dataSource")
public DataSource dataSource(DruidDataSource druidDataSource) {
    // DataSourceProxy for AT mode
    // return new DataSourceProxy(druidDataSource);
    // DataSourceProxyXA for XA mode
    return new DataSourceProxyXA(druidDataSource);
}


6、在发起分布式事务请求的方法上添加@GlobalTransactional 注解,开启全局事务

需要使用一个 @GlobalTransactional 注解在业务方法上:

@GlobalTransactional
public void purchase(String userId, String commodityCode, int orderCount) {
    ......
}


7、在发起分布式事务请求的方法上添加@GlobalTransactional 注解,开启全局事务

需要使用一个 @GlobalTransactional 注解在业务方法上:

@GlobalTransactional
public void purchase(String userId, String commodityCode, int orderCount) {
    ......
}


8、分支事务上,添加@Transactional注解

@Transactional(rollbackFor = Exception.class)
public void reduce(String userId, int money) {
    String xid = RootContext.getXID();
    log.info("reduce account balance in transaction: " + xid);
    jdbcTemplate.update("update account_tbl set money = money - ? where user_id = ?", new Object[] {money, userId});
    int balance = jdbcTemplate.queryForObject("select money from account_tbl where user_id = ?", new Object[] {userId}, Integer.class);
    log.info("balance after transaction: " + balance);
    if (balance < 0) {
        throw new RuntimeException("Not Enough Money ...");
    }
}


9、启动四个项目

57.png

10、发起请求校验分布式事务

发起购买请求:http://127.0.0.1:8020/purchase


基于初始化数据,和默认的调用逻辑,purchase 将可以被成功调用 3 次。


每次账户余额扣减 3000,由最初的 10000 减少到 1000。


第 4 次调用,因为账户余额不足,purchase 调用将失败。相应的:库存、订单、账户都回滚。


54.png53.png

55.png

54.png

56.png


项目地址:seata-xa


总结


本文主要介绍了seata xa的工作原理和实战使用方法。

相比其他seata其他3中模式,xa模式最大的优点就是业务无侵入,满足全局数据一致性。但相应的,xa模式需要数据库支持XA协议,性能较差,对长事务支持较差。


在当前的技术发展阶段,不存一个分布式事务处理机制可以完美满足所有场景的需求。

一致性、可靠性、易用性、性能等诸多方面的系统设计约束,需要用不同的事务处理机制去满足。

Seata 项目最核心的价值在于:构建一个全面解决分布式事务问题的标准化平台。

XA 模式的加入,补齐了 Seata 在 全局一致性 场景下的缺口,形成 AT、TCC、Saga、XA 四大 事务模式 的版图,基本可以满足所有场景的分布式事务处理诉求。

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
24天前
|
监控
Saga模式在分布式系统中保证事务的隔离性
Saga模式在分布式系统中保证事务的隔离性
|
1月前
|
存储 缓存 NoSQL
大数据-38 Redis 高并发下的分布式缓存 Redis简介 缓存场景 读写模式 旁路模式 穿透模式 缓存模式 基本概念等
大数据-38 Redis 高并发下的分布式缓存 Redis简介 缓存场景 读写模式 旁路模式 穿透模式 缓存模式 基本概念等
65 4
|
2月前
Saga模式在分布式系统中如何保证事务的隔离性
Saga模式在分布式系统中如何保证事务的隔离性
|
3月前
|
存储 分布式计算 算法
探索Hadoop的三种运行模式:单机模式、伪分布式模式和完全分布式模式
在配置Hadoop集群之前,了解这三种模式的特点、适用场景和配置差异是非常重要的。这有助于用户根据个人需求和资源情况,选择最适合自己的Hadoop运行模式。在最初的学习和开发阶段,单机模式和伪分布式模式能为用户提供便利和成本效益。进而,当用户要处理大规模数据集时,完全分布式模式将是理想的选择。
198 2
|
3月前
|
开发者 云计算 数据库
从桌面跃升至云端的华丽转身:深入解析如何运用WinForms与Azure的强大组合,解锁传统应用向现代化分布式系统演变的秘密,实现性能与安全性的双重飞跃——你不可不知的开发新模式
【8月更文挑战第31天】在数字化转型浪潮中,传统桌面应用面临新挑战。本文探讨如何融合Windows Forms(WinForms)与Microsoft Azure,助力应用向云端转型。通过Azure的虚拟机、容器及无服务器计算,可轻松解决性能瓶颈,满足全球用户需求。文中还提供了连接Azure数据库的示例代码,并介绍了集成Azure Storage和Functions的方法。尽管存在安全性、网络延迟及成本等问题,但合理设计架构可有效应对,帮助开发者构建高效可靠的现代应用。
32 0
|
1月前
|
NoSQL Java Redis
太惨痛: Redis 分布式锁 5个大坑,又大又深, 如何才能 避开 ?
Redis分布式锁在高并发场景下是重要的技术手段,但其实现过程中常遇到五大深坑:**原子性问题**、**连接耗尽问题**、**锁过期问题**、**锁失效问题**以及**锁分段问题**。这些问题不仅影响系统的稳定性和性能,还可能导致数据不一致。尼恩在实际项目中总结了这些坑,并提供了详细的解决方案,包括使用Lua脚本保证原子性、设置合理的锁过期时间和使用看门狗机制、以及通过锁分段提升性能。这些经验和技巧对面试和实际开发都有很大帮助,值得深入学习和实践。
太惨痛: Redis 分布式锁 5个大坑,又大又深, 如何才能 避开 ?
|
3月前
|
NoSQL Redis
基于Redis的高可用分布式锁——RedLock
这篇文章介绍了基于Redis的高可用分布式锁RedLock的概念、工作流程、获取和释放锁的方法,以及RedLock相比单机锁在高可用性上的优势,同时指出了其在某些特殊场景下的不足,并提到了ZooKeeper作为另一种实现分布式锁的方案。
114 2
基于Redis的高可用分布式锁——RedLock
|
3月前
|
缓存 NoSQL Java
SpringBoot整合Redis、以及缓存穿透、缓存雪崩、缓存击穿的理解分布式情况下如何添加分布式锁 【续篇】
这篇文章是关于如何在SpringBoot应用中整合Redis并处理分布式场景下的缓存问题,包括缓存穿透、缓存雪崩和缓存击穿。文章详细讨论了在分布式情况下如何添加分布式锁来解决缓存击穿问题,提供了加锁和解锁的实现过程,并展示了使用JMeter进行压力测试来验证锁机制有效性的方法。
SpringBoot整合Redis、以及缓存穿透、缓存雪崩、缓存击穿的理解分布式情况下如何添加分布式锁 【续篇】
|
15天前
|
NoSQL Redis
Redis分布式锁如何实现 ?
Redis分布式锁通过SETNX指令实现,确保仅在键不存在时设置值。此机制用于控制多个线程对共享资源的访问,避免并发冲突。然而,实际应用中需解决死锁、锁超时、归一化、可重入及阻塞等问题,以确保系统的稳定性和可靠性。解决方案包括设置锁超时、引入Watch Dog机制、使用ThreadLocal绑定加解锁操作、实现计数器支持可重入锁以及采用自旋锁思想处理阻塞请求。
52 16
|
1月前
|
缓存 NoSQL Java
大数据-50 Redis 分布式锁 乐观锁 Watch SETNX Lua Redisson分布式锁 Java实现分布式锁
大数据-50 Redis 分布式锁 乐观锁 Watch SETNX Lua Redisson分布式锁 Java实现分布式锁
61 3
大数据-50 Redis 分布式锁 乐观锁 Watch SETNX Lua Redisson分布式锁 Java实现分布式锁
下一篇
无影云桌面