用消息队列和消息应用状态表来消除分布式事务

Vite 任意文件读取漏洞复现(CVE-2026-39363) Vite开发服务器 WebSocket中的fetchModule方法未实施server.fs访问控制,导致攻击者可通过WebSocket连接并读取服务器上的任意文件(CVE-2026-39363) 阅读详情
由于数据量的巨大,大部分Web应用都需要部署很多个数据库实例。这样,有些用户操作就可能需要去修改多个数据库实例中的数据。传统的解决方法是使用分布式事务保证数据的全局一致性,经典的方法是使用两阶段提交协议。

长期以来,分布式事务提供的优雅的全局ACID保证麻醉了应用开发者的心灵,很多人都不敢越雷池一步,想像没有分布式事务的世界会是怎样。如今就如MySQL和PostgreSQL这类面向低端用户的开源数据库都支持分布式事务了,开发者更是沉醉其中,不去考虑分布式事务是否给系统带来了伤害。

事实上,有所得必有所失,分布式事务提供的ACID保证是以损害系统的可用性、性能与可伸缩性为代价的。只有在参与分布式事务的各个数据库实例都能够正常工作的前提下,分布式事务才能够顺利完成,只要有一个工作不正常,整个事务就不能完成。这样,系统的可用性就相当于参加分布式事务的各实例的可用性之积,实例越多,可用性下降越明显。从性能和可伸缩性角度看,首先是事务的总持续时间通常是各实例操作时间之和,因为一个事务中的各个操作通常是顺序执行的,这样事务的响应时间就会增加很多;其次是一般Web应用的事务都不大,单机操作时间也就几毫秒甚至不到1毫秒,一但涉及到分布式事务,提交时节点间的网络通信往返过程也为毫秒级别,对事务响应时间的影响也不可忽视。由于事务持续时间延长,事务对相关资源的锁定时间也相应增加,从而可能严重增加了并发冲突,影响到系统吞吐率和可伸缩性。

正是由于分布式事务有以上问题,eBay在设计上就不采用分布式事务,而是通过其它途径来解决数据一致性问题。其中使用的最重要的技术就是消息队列和消息应用状态表。

举个例子。假设系统中有以下两个表
  user(id, name, amt_sold, amt_bought)
  transaction(xid, seller_id, buyer_id, amount)
其中user表记录用户交易汇总信息,transaction表记录每个交易的详细信息。

这样,在进行一笔交易时,若使用事务,就需要对数据库进行以下操作:
begin;
  INSERT INTO transaction VALUES(xid, $seller_id, $buyer_id, $amount);
  UPDATE user SET amt_sold = amt_sold + $amount WHERE id = $seller_id;
  UPDATE user SET amt_bought = amt_bought + $amount WHERE id = $buyer_id;
commit;
即在transaction表中记录交易信息,然后更新卖家和买家的状态。

假设transaction表和user表存储在不同的节点上,那么上述事务就是一个分布式事务。要消除这一分布式事务,将它拆分成两个子事务,一个更新transaction表,一个更新user表是不行的,因为有可能transaction表更新成功后,更新user失败,系统将不能恢复到一致状态。

解决方案是使用消息队列。如下所示,先启动一个事务,更新transaction表后,并不直接去更新user表,而是将要对user表进行的更新插入到消息队列中。另外有一个异步任务轮询队列内容进行处理。
begin;
  INSERT INTO transaction VALUES(xid, $seller_id, $buyer_id, $amount);
  put_to_queue "update user("seller", $seller_id, amount);
  put_to_queue "update user("buyer", $buyer_id, amount);
commit;
for each message in queue
  begin;
    dequeue message;
    if message.type = "seller" then
      UPDATE user SET amt_sold = amt_sold + message.amount WHERE id = message.user_id;
    else
      UPDATE user SET amt_bought = amt_bought + message.amount WHERE id = message.user_id;
    end
  commit;
end

上述解决方案看似完美,实际上还没有解决分布式问题。为了使第一个事务不涉及分布式操作,消息队列必须与transaction表使用同一套存储资源,但为了使第二个事务是本地的,消息队列存储又必须与user表在一起。这两者是不可能同时满足的。

如果消息具有操作幂等性,也就是一个消息被应用多次与应用一次产生的效果是一样的话,上述问题是很好解决的,只要将消息队列放到transaction表一起,然后在第二个事务中,先应用消息,再从消息队列中删除。由于消息队列存储与user表不在一起,应用消息后,可能还没来得及将应用过的消息从队列中删除时系统就出故障了。这时系统恢复后会重新应用一次这一消息,由于幂等性,应用多次也能产生正确的结果。

但实际情况下,消息很难具有幂等性,比如上述的UPDATE操作,执行一次和执行多次的结束显然是不一样的。解决这一问题的方法是使用另一个表记录已经被成功应用的消息,并且这个表使用与user表相同的存储。假设增加以下表 message_applied(msg_id)记录被成功应用的消息,则产生最终的解决方案如下:
begin;
  INSERT INTO transaction VALUES(xid, $seller_id, $buyer_id, $amount);
  put_to_queue "update user("seller", $seller_id, amount);
  put_to_queue "update user("buyer", $buyer_id, amount);
commit;
for each message in queue
  begin;
    SELECT count(*) as cnt FROM message_applied WHERE msg_id = message.id;
    if cnt = 0 then 
      if message.type = "seller" then
        UPDATE user SET amt_sold = amt_sold + message.amount WHERE id = message.user_id;
      else
        UPDATE user SET amt_bought = amt_bought + message.amount WHERE id = message.user_id;
      end
      INSERT INTO message_applied VALUES(message.id);
    end
  commit;
  if 上述事务成功
    dequeue message
    DELETE FROM message_applied WHERE msg_id = message.id;
  end
end

我们来仔细分析一下:
1、消息队列与transaction使用同一实例,因此第一个事务不涉及分布式操作;
2、message_applied与user表在同一个实例中,也能保证一致性;
3、第二个事务结束后,dequeue message之前系统可能出故障,出故障后系统会重新从消息队列中取出这一消息,但通过message_applied表可以检查出来这一消息已经被应用过,跳过这一消息实现正确的行为;
4、最后将已经成功应用,且已经从消息队列中删除的消息从message_applied表中删除,可以将message_applied表保证在很小的状态(不清除也是可以的,不影响系统正确性)。由于消息队列与message_applied在不同实例上,dequeue message之后,将对应message_applied记录删除之前可能出故障。一但这时出现故障,message_applied表中会留下一些垃圾内容,但不影响系统正确性,另外这些垃圾内容也是可以正确清理的。

虽然由于没有分布式事务的强一致性保证,使用上述方案在系统发生故障时,系统将短时间内处于不一致状态。但基于消息队列和消息应用状态表,最终可以将系统恢复到一致。使用消息队列方案,解除了两个数据库实例之间的紧密耦合,其性能和可伸缩性是分布式事务不可比拟的。

当然,使用分布式事务有助于简化应用开发,使用消息队列明显需要更多的工作量,两者各有优缺点。个人观点是,对于时间紧迫或者对性能要求不高的系统,应采用分布式事务加快开发效率,对于时间需求不是很紧,对性能要求很高的系统,应考虑使用消息队列方案。对于原使用分布式事务,且系统已趋于稳定,性能要求高的系统,则可以使用消息队列方案进行重构来优化性能。


Android车载蓝牙开发全攻略:从基础到高级优化 本文系统介绍了Android车载蓝牙开发的核心技术与实践指南。文章首先阐述了蓝牙在智能汽车中的关键作用,包括免提通话、音频流媒体等应用场景。随后详细解析了蓝牙协议基础,重点介绍了HFP、A2DP等车载常用协议。开发实战部分提供了设备发现、连接、数据传输的完整代码示例,并融入多线程和内存优化技巧。通过真实车载案例,展示了启动时间、流畅度和资源使用的优化策略。最后针对面试需求,提供了专业的技术问答,涵盖蓝牙开发全流程。全文紧密结合Android车载开发职位要求,为开发者提供了一套完整的蓝牙技术解决方案。 阅读详情

相关推荐

OpenHarmony 4.1/5.0 Beta上手:如何用ArkUI在非华为设备上开发你的第一个“鸿蒙”应用

本文详细介绍了如何在非华为设备上使用ArkUI开发OpenHarmony应用,涵盖从设备选型、环境搭建到应用部署的全流程。通过实战案例展示ArkTS语言在开源鸿蒙生态中的应用,帮助开发者突破设备限制,构建跨平台鸿蒙应用。

weixin_33704234的博客 363

分布式事务,分布式锁,分布式系统幂等处理解决方案

什么是锁?对所有线程可见,原子性改变锁状态 synchronize的锁是对象,靠这个锁对象锁住代码块,只要拿到对象的线程执行锁住的代码块时,这个对象对所有线程可见,别的线程就不能执行该代码块,lock的锁是一个volatile修饰的int型变量,对所有线程可见,和原子性修改。 除了利用内存做锁,其他任何互斥的都能做锁(只考虑互斥情况),如流水表中的流水号与时间结合做幂等校验可以看做是一个不会释放的...

baihehaitangyijiu的博客 732

STM32-DAC定时器触发三角波生成【频率与幅度精准控制】

本文详细介绍了STM32 DAC定时器触发三角波生成的原理与实现方法,涵盖硬件配置、频率与幅度精准控制技巧及代码实现。通过定时器触发DAC模块,可灵活调节三角波的频率和幅度,适用于信号发生器等高精度应用场景。

weixin_29161785的博客 452

消除分布式事务的方法:消息队列和消息应用状态表

http://info.52z.com/html/28855.html 由于数据量的巨大,大部分Web应用都需要部署很多个数据库实例。这样,有些用户操作就可能需要去修改多个数据库实例中的数据。传统的解决方法是使用分布式事务保证数据的全局一致性,经典的方法是使用两阶段提交协议。 长期以来,分布式事务提供的优雅的全局ACID保证麻醉了应用开发者的心灵,很多人都不敢越雷池一步,想像没有分布...

vvgg 650

数理统计四大分布---正态分布、卡方分布、学生t分布和F分布

在统计学上,我们会遇到一些常见的分布,除了正态分布外,,如t检验对应的t分布,检验对应的分布,方差分析对应的F分布等。这些分布是统计学的基础,在假设检验、方差分析等领域都起着至关重要的作用。在此,我们对这四种分布做详细的介绍,并附上MATLAB代码。 四种分布的讲解摘自MATLAB 2016的统计学和机器学习工具箱,有兴趣的可以去查看英文原版。 另外,为了方便,上传文档为图片格式,可能有些模糊...

suncangan的博客 5万+

基于知识图谱的职位推荐系统的设计与实现

在职位推荐系统中,实体可以包括职位、公司、技能等,而关系可以包括职位与公司的关联、职位与技能的关联等。基于知识图谱的职位推荐系统可以提供更加智能和精确的职位推荐服务。通过收集和预处理数据构建知识图谱,并设计推荐算法和用户界面,可以实现一个完整的职位推荐系统。通过利用知识图谱技术,我们可以构建一个更加智能和精确的职位推荐系统。本文将介绍如何设计和实现基于知识图谱的职位推荐系统,并提供相应的源代码。通过定义实体和关系的节点和边,可以将数据存储在图数据库中,并建立它们之间的关系。

RiqaEditor的博客 1668

分布式事务与分布式锁相关问题

分布式事务的相关问题 2PC 和 3PC 的第一步到底是不是“类似”的? 2PC 的第一步投票(voting)阶段中,参与者收到事务执行询问请求时,就执行事务但不提交;而 3PC 却写着在 PreCommit 阶段执行事务不提交。2PC 和 3PC 的第一步,说它们类似,是指它们均是通过协调者,来询问参与者是否可以正常执行事务操作,参与者也都会给协调者回复。 在 2PC 中,如果所有参与者都返回结果后,会进入第二阶段,也就是提交阶段,也可以说是执行阶段,根据第一阶段的投票结果,进行提交或取消。 在

海陆云的博客 2万+

Python与西门子PLC数据交互实战:基于Snap7的跨平台工业通信指南

本文详细介绍了如何使用Python和Snap7库实现与西门子PLC的数据交互。内容涵盖跨平台环境搭建、连接参数配置、数据读写实战及性能优化技巧,为工业自动化、数据采集和智能制造场景提供了一套轻量、灵活且高效的通信解决方案。

vim8coder的博客 281

Redis分布式锁存在的问题

在很多场景中,我们为了保证数据的最终一致性,需要很多的技术方案来支持,比如分布式事务、分布式锁等。

小黑说Java 2569

语言随机效应模型_线性混合效应模型 VS 方差分析

线性混合效应模型相比方差分析具有很多优点。为了让大家更直观的了解,我们来做一下两者的对比。基于模拟的方法,生成了以下数据dat_sim:这种数据格式是长格式,也就是从实验软件导出来的那种数据格式。这种数据格式可以直接使用线性混合效应模型进行分析。该实验设计是被试内项目间的单因素两水平设计。category是自变量,两个水平分别是outgroup和ingroup,outgroup比ingr...

weixin_31960565的博客 2688

【数据库】SQL Server 2008 R2 安装全流程详解与常见问题避坑指南

本文详细解析了SQL Server 2008 R2的完整安装流程与核心配置。从安装前的系统兼容性检查、.NET Framework依赖准备,到图文详解实例配置、混合身份验证模式设置等关键步骤,并提供了安装失败、服务无法启动等高频问题的排查与避坑指南,帮助用户顺利完成部署。

java5的博客 1241

原子性和一致性的区别是什么?

注: 重度参考了:https://www.zhihu.com/question/30272728/answer/72476703(讲的非常好,逻辑清晰) 轻度参考了:https://www.cnblogs.com/fjdingsd/p/5273008.html(概念也比较清晰,只不过没有对比着这两个特性讲) 概念 原子性:一个事务内的操作,要么同时成功,要么同时...

weixin_30485379的博客 1225

FPGA实现PID算法

PID 控制器表达式如下(引用某硕士论文 《基于FPGA的PID控制器研究与实现》): 其中,Kp为比例增益,Ti为积分时间常数,Td为微分时间常数,u(t)为控制量,e(t)为偏差。 在数字控制系统中,PID控制规律的实现必须用数值逼近的方法。当采样周期相当短时,用求和代替积分、用后向差分代替微分,使模拟PID离散化变为差分方程。 本文使用并行结构来实现。具有延时小,计算速度快的特点,但需要资源比较多。 利用图3.2所示并行结构实现方法 ,进一步将3.8式分解: ...

nxin168的博客 1万+

[转载]用消息队列和消息应用状态表来消除分布式事务

由于数据量的巨大,大部分Web应用都需要部署很多个数据库实例。这样,有些用户操作就可能需要去修改多个数据库实例中的数据。传统的解决方法是使用分布式事务保证数据的全局一致性,经典的方法是使用两阶段提交协议。长期以来,分布式事务提供的优雅的全局ACID保证麻醉了应用开发者的心灵,很多人都不敢越雷池一步,想像没有分布式事务的世界会是怎样。如今就如MySQL和PostgreSQL这类面向低端用户的开源数据...

weixin_30784141的博客 109

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

用消息队列和消息应用状态表来消除分布式事务(转载)

由于数据量的巨大,大部分Web应用都需要部署很多个数据库实例。这样,有些用户操作就可能需要去修改多个数据库实例中的数据。传统的解决方法是使用分布式事务保证数据的全局一致性,经典的方法是使用两阶段提交协议。 长期以来,分布式事务提供的优雅的全局ACID保证麻醉了应用开发者的心灵,很多人都不敢越雷池一步,想像没有分布式事务的世界会是怎样。如今就如MySQL和PostgreSQL这类面向低端用户的开源...

y.a.l.e 169

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

上一篇: 多存储设备混合使用raid卡配置问题
下一篇: mysql show processlist命令 详解
笑天居士
博客等级 码龄24年 292粉丝 136原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值