最终一致分布式事务方案解析

news2025/1/11 5:52:14

业来主流的分布式事务的解决方案主要归位两大类:强一致性分布式事务和最终一致性分布式事务,本文不对强一致性分布式事务做过多描述,主要针对最终一致性方案解析。

根据笔者的工作经验来看,最终一致性方案适用用大部分互联网场景主要原因如下:

  • 由于微服务间无法直接进行数据访问,微服务间互相调用通常通过RPC(Dubbo)或Http API(Spring Cloud)进行,所以已经无法使用TM统一管理微服务的RM。
  • 不同的微服务使用的数据源类型可能完全不同,如果微服务使用了NoSQL之类不支持事务的数据库,则强一致性事务根本无从谈起。
  • 即使微服务使用的数据源都支持事务,那么如果使用一个大事务将许多微服务的事务管理起来,这个大事务维持的时间,将比本地事务长几个数量级。如此长时间的事务及跨服务的事务,将为产生很多锁及数据不可用,严重影响系统性能。

既然无法满足传统的ACID事务,在微服务下的事务管理必然要遵循新的法则--BASE理论。

BASE理论由eBay的架构师Dan Pritchett提出,BASE理论是对CAP理论的延伸,核心思想是即使无法做到强一致性,应用应该可以采用合适的方式达到最终一致性。BASE是指基本可用(Basically Available)、软状态( Soft State)、最终一致性( Eventual Consistency)。

  • 基本可用:指分布式系统在出现故障的时候,允许损失部分可用性,即保证核心可用。
  • 软状态:允许系统存在中间状态,而该中间状态不会影响系统整体可用性。分布式存储中一般一份数据至少会有三个副本,允许不同节点间副本同步的延时就是软状态的体现。
  • 最终一致性:最终一致性是指系统中的所有数据副本经过一定时间后,最终能够达到一致的状态。弱一致性和强一致性相反,最终一致性是弱一致性的一种特殊情况。

BASE中的最终一致性是对于微服务下的事务管理的根本要求,即虽然基于微服务的事务管理无法达到强一致性,但必须保证最终一致性,这就是所说的柔性事务。

实现事务最终一致性的方案主要有事件通知模式、事务补偿模式两种。

事件通知模式
事件通知模式的设计理念比较容易理解,即是主服务完成后将结果通过事件(常常是消息队列)传递给从服务,从服务在接受到消息后进行消费,完成业务,从而达到主服务与从服务间的消息一致性。

在这里插入图片描述
设计理念很简单,如果不考虑任何意外情况,上述的逻辑很完美,但是架构的设计主要是为了考虑异常情况。
考虑如下异常情况:

  • 在微服务的架构下,有可能出现网络IO问题或者服务器宕机的问题,如果这些问题出现在时序图的第7步,使得消息投递后无法正常通知主服务(网络问题),或无法继续提交事务(宕机),那么主服务将会认为消息投递失败,会滚主服务业务,然而实际上消息已经被从服务消费,那么就会造成主服务和从服务的数据不一致
  • 事件服务(在这里就是消息服务)与业务过于耦合,如果消息服务不可用,会导致业务不可用。应该将事件服务与业务解耦,独立出来异步执行,或者在业务执行后先尝试发送一次消息,如果消息发送失败,则降级为异步发送。

事件通知模式之本地异步事件服务模式
为了解决上述同步事件中描述的同步事件的问题,异步事件通知模式被发展了出来,既业务服务和事件服务解耦,事件异步进行,由单独的事件服务保证事件的可靠投递。

当业务执行时,在同一个本地事务中将事件写入本地事件表,同时投递该事件,如果事件投递成功,则将该事件从事件表中删除。如果投递失败,则由定时任务异步统一地处理投递失败的事件,进行重新投递,直到事件被正确投递,并将事件从事件表中删除。这个在本服务内部的定时任务一般叫它本地事件服务。这种方式最大可能地保证了事件投递的实效性,并且当第一次投递失败后,也能使用本地事件服务保证事件至少被投递一次。

这个方案原则的比较稳定,缺陷就是业务仍旧与事件服务有一定耦合(第一次同步投递时),更为严重的是,本地事务需要负责额外的事件表的操作,为数据库带来了压力,在高并发的场景,由于每一个业务操作就要产生相应的事件表操作,几乎将数据库的可用吞吐量砍了一半。正是因为这样的原因,本地异步事件服务模式进一步地发展,将事件服务独立出主业务服务,主业务服务不在对事件服务有任何强依赖。

  • 业务服务在提交前,向事件服务发送事件,事件服务只记录事件,并不发送。业务服务在提交或回滚后通知事件服务,事件服务发送事件或者删除事件。不用担心业务系统在提交或者会滚后宕机而无法发送确认事件给事件服务,因为事件服务会定时获取所有仍未发送的事件并且向业务系统查询,根据业务系统的返回来决定发送或者删除该事件。
  • 外部事件虽然能够将业务系统和事件系统解耦,但是也带来了额外的工作量:外部事件服务比起本地事件服务来说多了两次网络通信开销(提交前、提交/回滚后),同时也需要业务系统提供单独的查询接口给事件系统用来判断未发送事件的状态。

事件通知模式之基于MQ事件服务模式
上述说的基于外部事件服务来解耦,如果不想自己开发(当然自己开发也是开始的)。可以考虑使用MQ消息队列。假如使用的MQ本身支持事务消息,这样业务应用就能以某种方式确保消息正确投递到MQ。

消息事务的一种实现思路是通过保证多条消息的同时可见性来保证事务一致性。但是此类消息事务实现机制更多的是用在 consume-transform-produce(Kafka支持)场景中,其本质上还是用来保证消息自身事务,并没有把外部事务包含进来。

还有一种思路是依赖于 AMQP 协议(RabbitMQ支持)来确保消息发送成功。AMQP需要在发送事务消息时进行两阶段提交,首先进行 tx_select 开启事务,然后再进行消息发送,最后执行 tx_commit 或tx_rollback。这个过程可以保证在消息发送成功的同时,本地事务也一定成功执行。但事务粒度不好控制,而且会导致性能急剧下降,同时也无法解决本地事务执行与消息发送的原子性问题。

不过,RocketMQ事务消息设计解决了上述的本地事务执行与消息发送的原子性问题。在RocketMQ的设计中,broker和producer的双向通信能力使得broker天生可以作为一个事务协调者存在。而RocketMQ本身提供的存储机制,则为事务消息提供了持久化能力。RocketMQ 的高可用机制以及可靠消息设计,则为事务消息在系统在发生异常时,依然能够保证事务的最终一致性达成。

以rocketmq为例,事务消息的设计流程同样借鉴了两阶段提交理论,流程图如下:
在这里插入图片描述

  • 事务发起方首先发送 prepare 消息到 MQ。
  • 在发送 prepare 消息成功后执行本地事务。
  • 根据本地事务执行结果返回 commit 或者是 rollback。
  • 如果消息是 rollback,MQ 将删除该 prepare 消息不进行下发,如果是 commit 消息,MQ 将会把这个消息发送给 consumer 端。
  • 如果执行本地事务过程中,执行端挂掉,或者超时,MQ 将会不停的询问其同组的其他 producer 来获取状态。
  • Consumer 端的消费成功机制有 MQ 保证。

如果事务消息发送到MQ上后,会回调本地事务执行器;但是此时事务消息是prepare状态,对消费者还不可见,需要本地事务执行器返回RMQ一个确认消息。只有当确认完之后,才会将消息的状态由消费端不可见的prepare状态更新为消费者端可见的commied状态,如果本地事务执行器返回的是rollbak,则RMQ直接删除该prepare状态的消息。

为了处理由于异常情况导致RMQ收不到本地事务执行器的确认消息的问题,RMQ会通过服务器回查客户端Listener接口反查prepare状态事务消息最终应该的状态,从而将消息由消费端不可见的prepare状态更新为消费者端可见的commied状态或直接删除prepare状态的消息。

其实业界各种MQ均有各自的适用场景,很多通用性MQ关注点是消息的高效流转,如果加上事务消息这一特性会导致MQ的性能打一些折扣。而解决消息的可靠投递并不一定需要使用事务消息方案,采用上面介绍的两种方法也可以。一句话,技术上并不是一定要追求架构的最优,还是要考虑综合效能。

事件最大努力通知

上面说到的3种模式,均可以保证事件消息可靠地投递到下游服务那儿去。但有些场景是允许一定程度地丢消息的。于是就发展出事件最大努力通知模式。最大努力通知型的特点是,业务服务在提交事务后,进行有限次数(设置最大次数限制)的消息发送,比如发送三次消息,若三次消息发送都失败,则不予继续发送。所以有可能导致消息的丢失。同时,主业务方需要提供查询接口给从业务服务,用来恢复丢失消息。最大努力通知型对于时效性保证比较差(既可能会出现较长时间的软状态),所以对于数据一致性的时效性要求比较高的系统无法使用。这种模式通常使用在不同业务平台服务或者对于第三方业务服务的通知,如银行通知、商户通知等。

最后来个图整理本文设计的最终一致分布式事务方案的几种类型

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot 2.7.8 自定义 Starter 自动配置

文章目录SpringBoot 2.7.8 自定义 Starter前言本次练习的代码仓库代码简要说明custom-springboot-starter-demo 的pom文件customer-starter 的pom文件test 的pom文件配置类配置信息SpringBoot 2.7.8 自定义 Starter 前言 前段时间,SpringBoot 出 3.x 版本了。听说…

如何与他人交流 (如何跟老板提涨工资) 第16章

最重要的事情 ---强有力的论证上期我们说根据场景来优化策略,是在隔靴搔痒,然而一个容易给出理由的人很容易成为演讲的高手.说服别人的理性与感性举个例子,我们如果说服别人买一件商品。采用打广告的形式,往往有两种途径。比如说我们要买一辆车。展示它的…

Power BI瀑布图

瀑布图(Waterfall Plot)也被称为阶梯图,它出现的历史并不长,最初为麦肯锡所创,因自上而下形似瀑布而得名,面世之后以其展示效果清晰而流畅被广为接受,经常在经营和财务分析中使用。 瀑布图是根…

4d view软件 .vol .4dv转 dcom文件

一、 .vol转 dcom文件 1、4d view软件打开vol文件 2、settings--dicom configuration-add,设置如下(前面的alias、ae title等设的可以随便一些,我都设了1),然后save&exit(第6步设置也可以&#xff09…

Vue13-计算属性computed

首先使用methods方法实现属性计算 步入正题: 计算属性:拿已有的属性计算得出新的属性 1.vue中属性和计算属性是分开的,属性在data中,计算属性在computed中 computed中计算属性以对象的形式存贮 这里是将fullName以及get的返回值…

计算机网络基础学习指南(一)

前言 计算机网络基础是研发/运维工程师都需掌握的知识,但往往会被忽略。 今天,我将献上一份详细 & 清晰的计算机网络基础学习指南,涵盖 TCP / UDP协议、Http协议、Socket等,希望你们会喜欢。 1. 计算机网络体系结构 1.1 简…

深入理解ThreadLocal看这篇就够了-应用场景、内部原理、内存泄漏以及父子线程如何共享数据

为了帮助大家在项目中更好使用ThreadLocal,本文向大家介绍ThreadLocal原理和常见问题,具体内容如下:ThreadLocal是什么ThreadLocal的应用场景ThreadLocal的内部原理ThreadLocal内存泄露问题父子线程如何共享数据ThreadLocal是什么java.lang.T…

CCS10新建TMS320F28335工程

CCS10新建TMS320F28335工程 1. 新建工程 点击Project → New CCS Project。选择芯片类型(TMS320F28335)、仿真器类型(XDS200V3)、新建工程名称、选择新建一个空工程。 2. 配置工程选项 右键项目工程名,进入配置选项Proprrties。或者AltEnter打开配置选项。在工程…

gd32f103vbt6 串口OTA升级-问题记录-2-平衡OTA弊端

走在路上的时候,我想起了这个OTA的弊端,那我想有没有办法解决呢?其实是有的。 那就是我还是把app程序放在flash的最开始的位置,而把OTA的程序放到后面(flash的最后12k)去。 这样也带来新的弊端&#xff1…

PMP考试技巧PMP考试大纲

一,PMP解题策略 PMP考试默认条件 精准审题 E(Eye):找到题眼; K(Key):找到考点; C(Choice):确定 选项; 情景结合 基本知识从PMI的角度出发,按PMI的思维方式,项目经理应该做出什么决定; 选…

SpringBoot自定义全局异常处理

自定义全局异常处理一. 创建所需类1. 自定义异常接口2. 自定义枚举类3. 自定义异常类4. 自定义异常处理类5. 自定义全局响应类5.1 BaseResponse类5.2 RespGenerator类二. 效果演示我们在 SpringBoot 项目中,往往会写许多 Controler 接口类,由于 Controll…

SNV的使用

一:什么是SVN? SVN是一个版本控制系统,SVN全称Subversion,用于记录一个或多个文件内容变化,方便我们查阅特定版本的修改情况。以前在没有版本控制的时候,我们通常在项目根目录下这样命名项目:p…

Python 环境搭建配置

Python可应用于多平台包括 Linux 和 Mac OS X。你可以通过终端窗口输入 "python" 命令来查看本地是否已经安装Python以及Python的安装版本。Unix (Solaris, Linux, FreeBSD, AIX, HP/UX, SunOS, IRIX, 等等。)Win 9x/NT/2000Macintosh (Intel, PPC, 68K)OS/2DOS (多个…

微信小程序 Springboot校园达达互助平台快递代取系统 java

本 录 摘 要 III Abstract 1 1 系统概述 1 1.1 概述 2 1.2课题意义 3 1.3 主要内容 4 2 系统开发环境 5 2.1微信开发者工具 6 2.2小程序框架以及目录结构介绍 6 2.3 JAVA简介 7 2.4 MySQL数据库 7 3 需求分析 8 3.1 系统设计目标 8 3…

STM32开发(1)----stm32f103c6t6开发板介绍和环境搭建

stm32f103c6t6开发板介绍一、stm32f103c6t6芯片资源介绍STM32 的命名规则二、最小系统开发板介绍三、开发板基本使用方法软件安装MDK5 安装安装STM32芯片包安装licenseUSB转串口驱动安装四、本文小结一、stm32f103c6t6芯片资源介绍 stm32f103c6t6 是一款基于 ARM Cortex M3 内…

测试岗外包4年终上岸,这段日子说起来都是泪啊

昨天一个老哥找到我倾诉,他干了好几年外包,现在通过自己的努力应聘上了阿里测试开发,虽然只是P6,但也属实不容易了。这位老哥是湖南长沙毕业的,计算机专业,二流本科。长沙,湖南省会,…

Python图像合成与视频倒放

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录前言一、视频抓帧二、图像转换1,图像翻转2,图像文件倒序三、倒序视频合成四、图像截取拓展任务——动态图合成与倒放1,gif动态图生…

面了个阿里拿28k跳槽出来的,真正见识到了跳槽天花板

2022年已经结束了,迎来的是2023崭新的一年,最近内卷严重,各种跳槽裁员,相信很多小伙伴也在准备金三银四的面试计划。 作为一个入职5年的老人家,目前工资比较乐观,但是我还是会选择跳槽,因为感觉…

MySQL表中的联合查询

上一篇有关聚合查询的博客:MySQL表中的聚合查询_徐憨憨!的博客-CSDN博客主要是针对单个表进行查询操作,但是实际的开发环境中,数据往往来自己于不同的数据表,此时就需要使用联合查询进行操作!联合查询多表查询的基本执行过程:进行笛卡尔积然后设置条件删除无效数据进…

Echarts环形图线性渐变,hover后显示阴影

第004个点击查看专栏目录Echarts的渐变在上一篇文章中已经讲过 ECharts线性渐变色示例演示(2种渐变方式),这里做了环形图,饼图的一个渐变示例演示,hover到元素后,会呈现出一个阴影。注意,颜色属…