消息队列中的事务是什么呢?

消息队列中的事务是什么呢?

说到事务,肯定会优先想到数据库中的事务。在数据库中需要事务,是为了保证数据的一致性、完整性、持久性和隔离性。它可以将数据库中的一组操作合并为一个不可分割的工作单元,要么全部执行成功,要么全部执行失败。那消息队列为什么也需要事务呢?

在很多场景下,我们发消息的目的是为了通知另一个系统或者模块去更新数据,消息队列中的 “事务”,主要解决的是消息生产者和消息消费组的数据一致性问题。

拿电商举例,用户在电商 APP 上购物,先把商品加到购物车里,然后几件商品一起下单,最后支付,完成购物流程。

这个过程中,订单系统创建订单后,发消息给购物车系统,将已下单的商品从购物车中删除。其中,从购物车将已下单的商品删除这个步骤并不是主要流程中的步骤,故可以使用消息队列来异步清理购物车,这样的设计显得更加合理。

对于订单系统来说,它创建订单的过程实际上执行了 2 个步骤的操作:

  1. 在订单库中插入一条订单数据,创建订单;

  2. 发消息给消息队列,内容就是刚刚创建的订单。

购物车系统订阅相关的主题,接收订单发送的消息,然后清理购物车,在购物车中删除订单中的商品。

在分布式系统中,上述的所有操作都有可能会失败,如果不做任何处理,就有可能导致订单数据与购物车数据不一致的问题,比如:

  1. 创建了订单,没有删除购物车

  2. 订单没有创建,购物车里面的商品就被删除了

对于上面第一个问题来说,失败的处理比较简单,只要成功执行清理购物车后再提交消费确认即可,如果执行失败,由于没有提交消费确认,消息队列中不会删除该消息,消息队列会自动重试

问题的关键是第二个问题,创建订单和发送消息两个步骤要么都成功,要么都失败,不允许一个成功另一个失败的情况出现。

这就是事务需要解决的问题。

什么是分布式事务?

通常我们理解的事务是:对若干数据进行更新操作,为了保证这些数据的完整性和一致性,我们希望这些更新操作要么都成功,要么都失败;至于更新的数据,不只局限于数据库中的数据,可以是磁盘上的一个文件,也可以是远端的一个服务,或者以其他形式存储的数据。

一个严格意义的事务实现,应该具有四个特性:原子性、一致性、隔离性、持久性。这四个特性简称 ACID 特性。

原子性,是指一个事务操作不可分割,要么成功,要么失败,不能有一半成功一半失败的情况。

一致性,是指这些数据在事务执行完成这个时间点之前,读到的一定是更新前的数据,之后读到的一定是更新后的数据,不应该存在一个时刻,让用户读到更新过程中的数据。

隔离性,是指一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对正在进行的其他事务是隔离的,并发执行的各个事务之间不能互相干扰,这个有点儿像我们打网游中的副本,我们在副本中打的怪和掉的装备,与其他副本没有任何关联也不会互相影响。

持久性,是指一个事务一旦完成提交,后续的其他操作和故障都不会对事务的结果产生任何影响。

很多单体关系型数据库都实现了完整的 ACID,但是对于分布式系统来说,严格的实现 ACID 这四个特性几乎是不可能的,或者说实现的代价太大,大到我们无法接收。

分布式事务就是指在分布式系统中实现的事务。在分布式系统中,在保证可用性和不严重牺牲性能的情况下,要保证数据的一致性就非常困难了,所以出现了很多残缺版的一致性,比如顺序一致性、最终一致性等。

显然想要实现完整版的分布式系统事务更是不可能完成的任务。所以目前大家所说的分布式事务,更多情况下,是在分布式系统中事务的不完整实现。在不同的应用场景中,有不同的实现,目的都是通过一些妥协来解决实际问题。

在实际应用中,比较常见的分布式事务实现有 2PC(Two-phase Commit,也叫二阶段提交)、TCC(Try-Confirm-Cancel) 和事务消息。每一种实现都有其特定的使用场景,也有各自的问题,都不是完美的解决方案。

事务消息适合的场景主要是那些需要异步更新数据,并且对于数据实时性要求并不高的场景。比如上面提到的订单-购物车案例,在创建订单后,如果出现短暂的几秒,购物车里的商品没有被及时清空,也不是完全不可接受的,只要保证最终购物车的数据和订单数据保持一致即可。

消息队列是如何实现分布式事务的?

要使用事务消息,肯定需要消息队列提供相应的功能才能实现,Kafka 和 RocketMQ 都提供了事务相关功能。

还是订单-购物车的例子,我们一起看下如何使用消息队列来实现分布式事务。

首先,订单系统在消息队列中开启事务。然后订单系统向消息队列服务器发送一个 ”半消息“,这个半消息是一个完整的消息内容,与普通消息的唯一区别就是,在事务提交之前,这个消息对于消费者是不可见的。

半消息发送成功之后,订单系统开始执行本地事务,在订单库中创建一条订单记录,并提交订单库的数据库事务。然后根据本地事务执行结果决定提交或者回滚事务消息。

  • 如果订单创建成功,那就提交事务,购物车系统就可以消费到这条消息,继续后续的处理。

  • 如果订单创建失败,购物车系统就不会收到这条消息。

这样就基本实现了,”要么都成功,要么都失败“ 的一致性要求了。

半消息(也称为预提交消息)是通过一种两阶段提交的方式来确定事务是提交还是回滚的。

发送半消息的时,会包含一个标识,通常为事务 ID 或唯一标识,这个将于本地事务相关联。

如果本地事务执行成功,订单系统决定提交事务消息。它将在消息队列上的半消息标记为“可被消费”,这使得消费者可以看到和处理这条消息。

消费者可以使用事务标识来查找与该消息相关的本地事务状态,根据本地事务状态来决定是否要处理该消息。

上述过程中,还有一个问题是没有解决的:如果在第四步提交事务消息时失败了怎么办?

Kafka 的解决方案比较简单粗暴,直接抛出异常,让用户自行处理。我们可以在代码中反复重试提交,直到成功或者删除之前创建的订单作为补偿。RocketMQ 则给出了另一种解决方案。

RocketMQ 中的分布式事务实现

在 RocketMQ 中的事务实现中,增加了事务反查的机制来解决事务消息提交失败的问题。如果 Prodcuer 也就是订单系统,在提交事务或者回滚事务时发生网络异常,RocketMQ 的 Broker 没有收到提交或者回滚的请求。

Borker 会定期去 Producer 上反查这个事务对应的本地事务的状态,然后根据反查结果决定提交或者回滚这个事务。

为了支撑这个事务反查机制,我们的业务代码需要实现一个反查本地事务状态的接口,告知 RocketMQ 本地事务是成功还是失败。

在我们这个例子中,反查本地事务的逻辑很简单,只需要根据消息中的订单 ID,去订单库中查询是否存在即可,存在则返回成功,反之返回失败。RocketMQ 会自动根据事务反查的结果提交或回滚事务消息。

这个反查本地事务的实现,并不依赖消息的发送方,也就是订单服务的某个实例节点上的任务数据。这种情况下,即使是发送事务消息的那个订单服务节点宕机了,RocketMQ 依然可以通过查询其他服务节点来执行反查,确保事务的完成性。

综合上面讲的通用事务消息的实现和 RocketMQ 的事务反查机制,使用 RocketMQ 事务消息功能实现分布式事务的流程如下图:

小结

通过订单-购物车的案例,学习了事务的 ACID 四大特性,以及如何使用消息队列来实现分布式事务。

然后我们给出了现有的几种分布式事务的解决方案,包括事务消息,但这几种方案都不是银弹,每一种方案都有局限性和特定的使用场景。

最后我们学习了 RocketMQ 的事务反查机制,这张机制通过定期反查事务状态,来补充提交事务消息可能出现的失败问题。在 Kafka 中并没有实现类似的反查机制,需要用户自己去解决这个问题。

但是,这不代表 RocketMQ 的事务功能比 Kafka 更好,只能说在我们这个例子的场景下,更适合使用 RocketMQ。

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

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

相关文章

No191.精选前端面试题,享受每天的挑战和学习

🤍 前端开发工程师(主业)、技术博主(副业)、已过CET6 🍨 阿珊和她的猫_CSDN个人主页 🕠 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 🍚 蓝桥云课签约作者、已在蓝桥云课上架的前后端实战课程《Vue.js 和 Egg.js 开发企业级健康管理项目》、《带你从入…

千年密码新解读,DeepMind 开发 Ithaca 破译希腊铭文

铭文、碑刻是过去文明的思想、文化和语言的体现。金石学家破译千年前的密码,需要完成文本修复、时间归因和地域归因三大任务。 主流的研究方式是「字符串匹配」,即凭借记忆或查询语料库匹配字型相似的铭文,这导致了结果的混淆和误判。 为此…

酷柚易汛ERP - 其他出库单操作指南

1、应用场景 处理其他非销售类型的出库单据,比如内部领用福利、赔偿、借出、领用材料、以货抵债等不参与销售管理的出库类业务。 2、主要操作 2.1 新增其他出库单 打开【仓库】-【其他出库单】,新增单据 出库单位成本及出库成本不能录入;…

msvcr71.dll丢失多种解决方法解析,全方位解读msvcr71.dll文件

在日常使用电脑时,你是否曾遇到过“msvcr71.dll文件丢失”的错误提示?别着急,本文将为你详细介绍msvcr71.dll丢失的解决方法,让你迅速解决这一烦恼。 一.多种msvcr71.dll丢失解决方法 修复方法一:重新安装相应软件 首先&#xf…

推荐一份适合所有人做的副业,尤其是程序员。

我建议每个人都去尝试一下网上接单,这是一个门槛低、类型多样的方式,尤其适合程序员! 在接单平台上,你可以看到各种类型的兼职。以freelancer为例,你可以在这里找到技术、设计、写作等类型的兼职,只要发挥…

Linux SSH免密登录

目录 简介 创建Linux用户和用户组 配置LINUX静态IP 编辑IP映射 SSH免密登录配置 登录测试 简介 SSH(Secure shell)是可以在应用程序中提供安全通信的一个协议,通过SSH可以安全地进行网络数据传输,它的主要原理是利用非对称加密…

常见光模块的封装有哪些呢?

光模块的封装,保障了光通信的稳定和可靠性。本文介绍几种常见的光模块的封装类型。 19封装–焊接型光模块,一般速度不高于千兆,多采用SC接口。 SFP封装:SFP(Small form-factor pluggable)意思是小型可拔插…

金蝶云星空将子窗体的内容传回到父窗体的开发实现

文章目录 金蝶云星空将子窗体的内容传回到父窗体的开发实现前置任务父窗体打开子窗体时传入回调函数子窗体设置返回参数父窗体接收参数在回调函数根据参数做一些逻辑处理 金蝶云星空将子窗体的内容传回到父窗体的开发实现 前置任务 金蝶云星空表单插件实现父窗体打开子窗体&a…

合合信息亮相新加坡科技周——Big Data AI World Expo展示AI驱动文档数字化的前沿能力

展会规模背景: 2023年10月11日-12日,合合信息在TECH WEEK SINGAPORE(新加坡科技周)亮相,并在人工智能世界博览会(Big Data & AI World)展示合合信息核心人工智能文字识别技术能力。合合信息…

无人机航迹规划:五种最新智能优化算法(KOA、COA、LSO、GRO、LO)求解无人机路径规划MATLAB

一、五种算法(KOA、COA、LSO、GRO、LO)简介 1、开普勒优化算法KOA 开普勒优化算法(Kepler optimization algorithm,KOA)由Mohamed Abdel-Basset等人于2023年提出。五种最新优化算法(SWO、ZOA、EVO、KOA、…

上传文件大小限制报错

做了一个上传文件的功能,前端通过文件流程的形式调用后台接口。几百k的文件能成功,几M的文件会失败。原因有二 第一是被nginx限制,请求也无法转发到应用服务器里面(也就是tomcat看不到请求日志只有nginx日志能看到请求&#xff0…

设计模式之建造者(Builder)

用来构建复杂对象 分离复杂对象的构建和表示 同样的构建过程可以创建不同的表示 当有一个复杂的对象,构建它的时候,比较复杂 例如,构建一个对象,要传入50个参数,可以先构建part1,再构建part2…最后调用bu…

Git-工作流

前言 一、工作流概述二、Git flow1.主要流程2.优缺点3.适用场景 三、Github flow1.主要流程2.优缺点3.适用场景 四、Gitlab flow1.主要流程2.优缺点3.适用场景 总结参考 一、工作流概述 开发人员通过Git可以记录和追踪代码的变化,包括添加、删除和修改文件。如果是…

【Python 算法】双向迪杰斯特拉算法 Python实现

双向迪杰斯特拉算法Python实现 文章目录 双向迪杰斯特拉算法Python实现简介双向迪杰斯特拉算法优势局限性算法的基本步骤终止条件 基本步骤伪代码Python 实现 简介 双向迪杰斯特拉算法(Bi Directional Dijkstra Algorithm)是一种用于在加权图中查找两个…

红色旅游AR互动体验将景区推向更广泛的市场

AR技术的出现使得各展厅观众可以在虚拟和现实的层面进行互动,利用AR和VR技术,将展览地点扩展到特定的虚拟领域,实现了"无触觉"交互体验,增强现实技术和展馆的对接更加激发人们了解新事物的兴趣。 一、AR景区&#xff1a…

【03】Istio Gateway示例配置

3.1 开放kiali至集群外部 首先将istio-inressateway暴露集群外部; 在node02的ens33网卡上面有多余的ip地址,将该地址绑定在igressgateway的svc 上面。 kubectl edit svc istio-ingressgateway -n istio-system定义kiali的ingress gateway的资源配置清单 apiVersion:…

第十六章,反射与注解例题

package 例题; import java.lang.reflect.Constructor;class 例题1Demo {//变量String s;int i, i2, i3;private 例题1Demo() {//无参构造方法}protected 例题1Demo(String s, int i) {//有参构造方法this.s s;this.i i;}public 例题1Demo(String... strings) throws NumberF…

堆排序(小根堆模板)

输入一个长度为 n 的整数数列,从小到大输出前 m 小的数。 输入格式 第一行包含整数 n 和 m。 第二行包含 n 个整数,表示整数数列。 输出格式 共一行,包含 m 个整数,表示整数数列中前 m 小的数。 数据范围 1≤m≤n≤10^5&am…

Centos8上部署Zabbix5.0

1.关闭Selinux及防火墙,避免Web页面无法访问。 setenforce 0 vim /etc/selinux/config 修改“SELINUX”等号后的内容为disabled SELINUXdisabled\\关闭并关闭开机自启 systemctl stop firewalld systemctl disable firewalld 2.配置Centos8本地yum源。 mkdir /mn…

『MySQL快速上手』-⑦-内置函数

文章目录 1.日期函数1.1 获得年月日1.2 获得时分秒1.3 获得时间戳1.4 在日期的基础上加日期1.5 在日期的基础上减去时间1.6 计算两个日期之间相差多少天案例1案例22.字符串函数案例3.数学函数4.其他函数1.日期函数 1.1 获得年月日