一文搞懂分布式事务解决方案

前言

在这里插入图片描述

在当今的分布式系统中,分布式事务管理是一个关键挑战。在面对跨多个服务的复杂业务流程时,确保数据一致性和事务的原子性变得至关重要。本文将深入探讨分布式事务的概念、原理、实现方式以及在Java领域的应用。

什么是分布式事务

分布式事务是指涉及多个独立的系统或服务的事务操作。在分布式系统中,由于数据存储在不同的数据库或服务中,并且跨越多个计算节点,因此需要跨系统进行事务协调和管理,以确保事务的原子性、一致性、隔离性和持久性(ACID原则)。

可能语言文字描述的有点抽象,我用几张图片,教你弄懂分布式事务。

传统单体架构

在互联网发展初期,单体架构完全可以满足现有业务需求,所有的业务共用一个数据库,整个业务流程或许只用在一个方法里同一个事务下操作数据库即可。此时做到所有操作要么全部提交 或 要么全部回滚很容易。

@transactional
public void demoService(UserInfo userInfo) {
	// 1.用户付款
	userMapper.payMoney(userInfo);
	// 2.订单增加
	orderMapper.addItem(userInfo);
}

在这里插入图片描述
可随着业务量的不断增长,单体架构渐渐扛不住巨大的流量,时间久了,各种各样的问题自然而然的也出现了:复杂性高,部署频率低,可靠性差,扩展能力受限,此时就需要对数据库、表做分库分表处理,将应用服务进行拆分。也就产生了用户服务、订单服务、库存服务等,由此带来的问题就是服务与服务之间的独立部署,互相隔离,每个微服务都维护着自己的数据库,服务之间的调用只能通过RPC远程掉调用,此时单体架构的数据库事务就无法做到全局事务的管理。当用户再次进行付款服务的时候,此时不同的服务只能保证各自的数据库事务。所以为了保证整个支付流程的数据一致性,就需要分布式事务了。

分布式部署架构

在这里插入图片描述
了解分布式事务,就不得不提一下分布式事务中比较经典的一些理论知识。

CAP理论

CAP 也就是 Consistency(一致性)、Availability(可用性)、Partition Tolerance(分区容错性)

在理论计算机科学中,CAP 定理指出对于一个分布式系统来说,当设计读写操作时,只能同时满足以下三点中的两个:一致性(Consistency) : 所有节点访问同一份最新的数据副本可用性(Availability): 非故障的节点在合理的时间内返回合理的响应(不是错误或者超时的响应)。分区容错性(Partition Tolerance) : 分布式系统出现网络分区的时候,仍然能够对外提供服务。

分布式系统中,多个节点之间的网络本来是连通的,但是因为某些故障(比如部分节点网络出了问题)某些节点之间不连通了,整个网络就分成了几块区域,这就叫网络分区。CAP 目前来说无法都兼备,因此当前微服务策略中要么要么CP,或者AP。

BASE理论

这个时候又有一个理论出现了,那就是 BASE理论 。它是用来对 CAP理论 进行一些补充,

BA(Basically Available):基本可用
S(Soft State):软状态
E(Eventually Consistent):最终一致性
这个理论的核心思想便是:如果我们如法做到强一致性,那么每个应用都应该根据自身的业务特点,采用适当的方式来使系统达到最终一致性。

分布式事务解决方案

所以在分布式背景下,两个服务之间相互调用,使用的是不用的数据库,这种情况下肯定会出现分布式事务的问题。

2PC(两阶段提交)

基于 XA 协议实现的分布式事务,XA 协议中分为两部分:事务管理器和本地资源管理器。

两阶段提交是把整个事务提交分为 prepare 和 commit 两个阶段以支付系统为例,分布式系统中有用户、订单和库存三个服务,如下图:

第一阶段:prepare 阶段
在这里插入图片描述
第二阶段:commit 阶段
在这里插入图片描述
两阶段提交(2PC),对业务侵⼊很小,它最⼤的优势就是对使⽤⽅透明,用户可以像使⽤本地事务⼀样使⽤基于 XA 协议的分布式事务,能够严格保障事务 ACID 特性。

2PC的缺点也是显而易见,它是一个强一致性的同步阻塞协议,事务执⾏过程中需要将所需资源全部锁定,也就是俗称的刚性事务。所以它比较适⽤于执⾏时间确定的短事务,整体性能比较差。

一旦事务协调者宕机或者发生网络抖动,会让参与者一直处于锁定资源的状态或者只有一部分参与者提交成功,导致数据的不一致。因此,在⾼并发性能⾄上的场景中,基于 XA 协议的分布式事务并不是最佳选择。

3PC(三阶段提交)

三段提交(3PC)是二阶段提交(2PC)的一种改进版本 ,为解决两阶段提交协议的阻塞问题,上边提到两段提交,当协调者崩溃时,参与者不能做出最后的选择,就会一直保持阻塞锁定资源。

为了解决两阶段提交的问题,三阶段提交做了改进:

  1. 在协调节点和事务参与者都引入了超时机制。
  2. 第一阶段的 prepare 阶段分成了两步,canCommit 和 preCommit。

第一阶段:canCommit
在这里插入图片描述
第二阶段:preCommit
在这里插入图片描述
第三阶段:commit 阶段
在这里插入图片描述
虽然 3PC 用超时机制,解决了协调者故障后参与者的阻塞问题,但与此同时却多了一次网络通信,性能上反而变得更差,如果第三阶段发出 rollback 请求,有的节点没有收到,那没有收到的节点会在超时之后进行提交,造成数据不一致。

TCC (Try Confirm Cancel) 事务补偿

TCC它是属于补偿型分布式事务。它的核心思想是 针对每个操作,都要注册一个与其对应的确认和补偿操作。TCC 实现分布式事务一共有三个步骤:分别指 Try、Confirm、Cancel ,一个业务操作要对应的写这三个方法。

TCC 不存在资源阻塞的问题,因为每个方法都直接进行事务的提交,一旦出现异常通过则 Cancel 来进行回滚补偿,这也就是常说的补偿性事务。

TCC是基于业务层面做的分布式事务,最终目的是达到数据最终一致性,是一种柔性事务。

下面以一个例子来说明三个阶段需要做的事:比如现在有两个数据库,一个用户账户数据库、一个商品库存数据库,现在提供一个买货的接口,当买卖成功时,扣除用户账户和商品库存,大致伪代码如下:

public void buy() {
    // 用户账户操作
    userAccount();
    // 商品账户操作
    StoreAccount();
}

在上面这个操作做,两个函数的操作必须同时成功,不然就会出现数据不一致问题,也就是需要保证事务原子性。因为设定的场景是数据在两个不同的数据库,所有没有办法利用单个数据库的事务机制,它是跨数据库的,所以需要分布式事务的机制。

class Demo {
    
    public void buy() {
        // try 阶段:比如去判断用户和商品的余额和存款是否充足,进行预扣款和预减库存
        if (!userServer.tryDeductAccount()) {
            // 用户预扣款失败,相关数据没有改变,返回错误即可
        }
        if (!storeService.tryDeductAccount()) {
            // cancel 阶段: 商品预减库存失败,因为前面进行了用户预扣款,所以需要进入cancel阶段,恢复用户账户
            userService.cancelDeductAccount();
        }

        // Confirm 阶段:try 成功就进行confirm阶段,这部分操作比如是将扣款成功状态和减库存状态设置为完成
        if (!userService.confirmDeductAccount() || !storeService.confirmDeductAccount()) {
            // cancel 阶段:confirm的任意阶段失败了,需要进行数据恢复(回滚)
            userService.cancelDeductAccount();
            storeService.cancelDeductAccount();
        }
    }
}

可以看出,如果后续增加更多的业务处理,就会重复添加三个阶段的业务代码,代码侵入量高。

业务状态补偿

业务状态补偿是通过给业务数据添加状态字段,在数据库中认为当前服务执行成功,标识成功,通过调用其它服务返回的业务结果,来对其本地业务进行补偿,失败则进行一些补偿操作,更新状态,从从而达到最终的数据一致性,这种方案大多在支付、银行业务场景见的比较多,很依赖业务实现方案。

MQ消息事务

消息事务的原理是 将两个事务通过消息中间件来进行异步解耦。基于可靠消息服务的方案是通过消息中间件来保证上、下游应用数据操作的一致性。假设有 订单服务、库存服务,分布可以处理 订单、库存两个任务,此时需要存在一个业务流程,将任务 订单和库存 放到同一个事物中处理,这种方式就可以借助消息中间件来实现。
在这里插入图片描述
上面大致分为两个步骤:

  1. 步骤一: 订单服务向消息中间件发布消息
    1.在订单服务开始前,首先向消息中间件发送一条信息,告诉MQ自己即将开始执行相应的业务操作
    2.消息中间件收到后将该消息持久化,但不进行投递。持久化成功后,向订单服务返回确认应答
    3.订单服务收到确认应答后,便可以开始处理对应的业务
    4.订单服务处理完后,便会向消息中间件发送 Commit 或者 Rollback 请求,该请求发送完成后,订单服务业务就算处理完成,该事务的处理过程也就结束了
    5.在消息中间件收到 Commit 后,便会向 库存服务投递消息,如果收到 Rollback 便会直接丢弃消息

如果消息中间件在最后的过程中,长时间没有收到服务A 发送的 Commit 或 Rollback 指令,这个时候就需要依靠 超时询问机制

超时询问机制:
订单服务除了实现正常的业务流程之外,还是需要提供一个可供消息中间件事务询问的接口。在消息中间件第一次收到消息后便会开始计时,如果超过规定的时间没有收到后续的指令,就会主动调用订单服务提供的事务询问接口,询问当前服务的状态,通常来说该接口会返回三种结果,中间件需要根据这三种不同的结果做出不同的处理:
提交:直接将该消息投递给服务B
回滚:直接将该消息丢弃
处理中:继续等待,重新计时

  1. 步骤二: 消息中间件向库存服务投递消息
    1.消息中间件收到订单服务的提交 Commit 指令后便会将该消息投递给库存服务,然后将自己的状态置为阻塞等待状态。库存服务收到消息中间件发送的消息后便开始处理自己的业务逻辑,处理完成后便会向消息中间件发出回应。但是在消息中间件阻塞等待的时候同样会出现问题
    正常情况:消息中间件投递完消息后,进入阻塞等待状态,在收到确认应答后便认为事务处理完成,该流程结束
    等待超时情况:在等待确认应答超时之后就会重新进行投递,直到库存服务器返回消费成功响应为止。而消息重试的次数和时间间隔都可以设置,如果最终还是不能成功进行投递,则需要人工干预。

由此可以看出来,MQ消息事务方案是实现了最终一致性,适用于高并发的场景。RocketMQ 就很好的支持了消息事务。如果只是为了实现MQ事务而引入MQ事务,势必会增加业务的复杂性,如果业务本身就因为其它需求,使用到了RocketMQ,消息事务方法不失为一种好的解决办法。

Seata (阿里开源分布式解决方案)

Seata是一个分布式事务解决方案,致力于解决分布式系统中的事务一致性问题。它提供了高效、易用的分布式事务管理功能,包括事务发起、全局事务管理、分支事务管理等。Seata具有原子性、一致性、隔离性和持久性(ACID)的特性

Seata 的设计目标是对业务无侵入,因此它是从业务无侵入的两阶段提交(全局事务)着手,在传统的两阶段上进行改进,他把一个分布式事务理解成一个包含了若干分支事务的全局事务。而全局事务的职责是协调它管理的分支事务达成一致性,要么一起成功提交,要么一起失败回滚。

Seata 架构设计

  1. TC(Transaction Coordinator):事务协调者。管理全局的分支事务的状态,用于全局性事务的提交和回滚。
  2. TM(Transaction Manager):事务管理者。用于开启、提交或回滚事务。
  3. RM(Resource Manager):资源管理器。用于分支事务上的资源管理,向 TC 注册分支事务,上报分支事务的状态,接收 TC 的命令来提交或者回滚分支事务。
    在这里插入图片描述
    1.订单服务中的 TM 向 TC 申请开启一个全局事务,TC 就会创建一个全局事务并返回一个唯一的 XID
    2.订单服务中的 RM 向 TC 注册分支事务,然后将这个分支事务纳入 XID 对应的全局事务
    3.订单服务开始执行分支事务
    4.订单服务开始远程调用库存服务,此时 XID 会根据调用链进行传播
    5.订单服务中的 RM 也向 TC 注册分支事务,然后将这个分支事务纳入 XID 对应的全局事务管辖中
    6.订单服务开始执行分支事务
    7.全局事务调用处理结束后,TM 会根据有误异常情况,向 TC 发起全局事务的提交或回滚
    8.TC 协调其管辖之下的所有分支事务,决定是提交还是回滚
SpringBoot集成Seata

// 在引入seata之前,需要下载相关服务,在官网下载即可。

引入 seata 的 maven 依赖坐标,需要开启事务的方法上添加 @GlobalTransactional 注解,类似于我们单体事务添加的@Transactional。

@GlobalTransactional 
// 支付系统伪代码
public String demoService(UserInfoDTO dto) {
	// 订单服务
	orderService.addOrder(dto);
	// 库存服务
	// 模拟远程调用库存服务
}

Seata 作为一个强大的分布式事务解决方案,为我们提供了一种简单而灵活的工具来解决分布式事务问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/462008.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

leetcode代码记录(动态规划基础题(斐波那契数列)

目录 1. 题目:2. 斐波那契数列:小结: 1. 题目: 斐波那契数 (通常用 F(n) 表示)形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是&#xff1a…

【Spring Boot】创建你的第一个 Spring Boot 应用

创建你的第一个 Spring Boot 应用 1.环境配置2.步骤详解3.项目结构分析3.1 入口类 DemoApplication3.2 控制器 PathVariableController3.3 控制器 BasicController3.4 模型 User 4.运行 Spring Boot 目前已经成为了 Java 开发领域的框架范式。本篇博客,我将带领大家…

c语言:操作符详解(上)

目录 一、操作符的分类二、二进制和进制转换1.2进制转10进制2.10进制转2进制3.2进制转8进制4.2进制转16进制 三、原码、反码、补码四、算术操作符、-、*、/、%1.**和-**2.*3./4.% 五、移位操作符1.左移操作符2.右移操作符 六、位操作符:&、|、^、~七、赋值操作符…

【Linux进程状态】

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 目录 前言 一、直接谈论Linux的进程状态 看看Linux内核源代码怎么说 1.1、R状态 -----> 进程运行的状态 1.2、S状态 -----> 休眠状态(进程在等待“资源”就绪) 1.3、T状…

想兼职赚钱?盘点6个靠谱兼职,赚钱更轻松!

1,微头条搬砖 微头条搬砖是一个门槛不高的赚钱方式,而且不需要你有多么好的原创能力,去收集一些热门文章的素材进行文章伪原创,十分钟就能搞定,只要你的文章有爆点,足够吸人眼球,就能够获取不低…

liunx离线安装mysql

liunx离线安装mysql 一.安装二.添加系统mysql组和mysql用户三.创建并修改mysql数据目录四.修改目录权限五.初始化数据库如果报错关于libaio.so.1六.修改权限为root七.添加启动服务**八.** ***\*登录数据库\**** 博主 默语带您 Go to New World. ✍ 个人主页—— 默语 的博客&a…

OpenHarmony 鸿蒙操作系统初体验

一、测试环境 硬件:野火开发板 RK3568 ,LubanCat2-N 系统:OpenHarmony_3.2.3 二、驱动安装 下载驱动 Rockchip_DriverAssitant_v5.1.1,并安装。 安装完成 三、镜像烧录 准备工作 注:仅支持烧录镜像到EMMC&…

Arthas使用案例(二)

说明:记录一次使用Arthas排查测试环境正在运行的项目BUG; 场景 有一个定时任务,该定时任务是定时去拉取某FTP服务器上的文件,进行备份、读取、解析等一系列操作。 而现在,因为开发环境是Windows, 线上项…

代码随想录 -- 回溯算法

文章目录 回溯算法理论什么是回溯法回溯法的效率回溯法解决的问题理解回溯法回溯法模板 组合问题I描述题解优化 组合总和III描述题解 电话号码的字母组合描述题解 组合总和描述题解 组合总和II描述题解 分割回文串描述题解 复原IP地址描述题解 子集描述题解 子集II描述题解 递增…

Linux 学习笔记(16)

十六、 计划任务 在很多时候为了自动化管理系统,我们都会用到计划任务,比如关机,管理,备份之类的操作,我 们都可以使用计划任务来完成,这样可以是管理员的工作量大大降低,而且可靠度更好。 l…

软件测试之学习测试用例的设计(等价类法、边界值法、错误猜测法、场景法、因果图法、正交法)

1. 测试用例的概念 软件测试人员向被测试系统提供的一组数据的集合,包括 测试环境、测试步骤、测试数据、预期结果 2. 为什么在测试前要设计测试用例 测试用例是执行测试的依据 在回归测试的时候可以进行复用 是自动化测试编写测试脚本的依据 衡量需求的覆盖率…

通过简单的案例入门Mybatis~

目录 一.概述 二.JDBC的缺点 三.案例 1.创建测试类 2.加载Mybatis核心配置文件获取SqlSessionFactory 3.获取SqlSession对象 4.执行sql 5.释放资源 一.概述 Mybatis是一款持久层框架,用于简化JDBC开发。所谓框架,就是一个半成品软件,…

2024年【P气瓶充装】考试资料及P气瓶充装考试总结

题库来源:安全生产模拟考试一点通公众号小程序 2024年P气瓶充装考试资料为正在备考P气瓶充装操作证的学员准备的理论考试专题,每个月更新的P气瓶充装考试总结祝您顺利通过P气瓶充装考试。 1、【多选题】CNG双燃料汽车系统主要包括(&#xff…

数据链路层_以太网

IP协议确定数据跨网络从主机A到主机B的路径,即IP协议解决了路径选择问题,但在这之前,必须先解决数据在一个子网内的传输的问题。跨网络的本质就是跨多个子网,只要一个子网内可以通信,那么便可以跨网络通信。 一.以太…

Echarts+Vue 首页大屏静态示例Demo 第四版 支持自适应

效果: 源码: <template><ScaleScreenclass="scale-wrap":selfAdaption="true":autoScale="true":class="{ fullscreen-container: isFullScreen }"><div class="bg"><dv-loading v-if="loading&…

Docker 中 MySQL 的部署与管理

目录 一、Docker 中部署 MySQL1.1 部署 MySQL1.2 进入容器并创建数据库1.3 Navicat 可视化工具连接 二、可能存在的问题2.1 1130 - Host ‘172.17.0.1‘ is not allowed to connect to this MySQL server 参考资料 一、Docker 中部署 MySQL 1.1 部署 MySQL 首先&#xff0c;从…

[题解]无厘头题目——无聊的军官

这道题非常无厘头&#xff01; 题目描述&#xff1a; 每个学年的开始&#xff0c;高一新生们都要进行传统的军训。今年有一个军训教官十分奇怪&#xff0c;他为了测试学员们的反应能力&#xff0c;每次吹哨后学员们都会变换位置。每次左数第I位学员都会站到第ai个位置&#x…

代码随想录训练营Day25:● 216.组合总和III ● 17.电话号码的字母组合

216.组合总和III 题目链接 https://leetcode.cn/problems/combination-sum-iii/description/ 题目描述 思路 自己写的效率会慢一些&#xff0c;而且没有用到剪枝 class Solution {List<List<Integer>> list new ArrayList<>();List<Integer> lis…

python知识点总结(一)

这里写目录标题 一、什么是WSGI,uwsgi,uWSGI1、WSGI2、uWSGI3、uwsgi 二、python中为什么没有函数重载&#xff1f;三、Python中如何跨模块共享全局变量?四、内存泄露是什么?如何避免?五、谈谈lambda函数作用?六、写一个函数实现字符串反转&#xff0c;尽可能写出你知道的所…

『scrapy爬虫』05. 使用管道将数据写入mysql(详细注释步骤)

目录 1. 新建管道类,并启用2. 准备好mysql数据库新建表3. 实现管道写入数据库的代码测试一下 总结 欢迎关注 『scrapy爬虫』 专栏&#xff0c;持续更新中 欢迎关注 『scrapy爬虫』 专栏&#xff0c;持续更新中 如果对mysql和python不熟悉可看专栏【Python之pymysql库学习】 1.…