分布式事务解决方案:6个生活中的小故事

前言

分布式事务听起来很高大上,其实背后的道理并不复杂。

简单说就是:一个事情要分成好几步做,每一步都可能失败,怎么保证整个事情要么全部完成,要么就像没发生过一样?

为了讲清楚这个问题,我找了6个生活中的场景来打比方。看完你会发现,分布式事务的解决方案,其实我们每天都在用。


一、2PC(两阶段提交):老板发工资的故事

故事场景

你是公司的财务主管,要给全公司100个员工发工资。你担心万一银行系统出问题,有的人发了有的人没发,那就麻烦了。

两阶段操作

第一阶段:提前通知

你先给银行打个电话:"请帮我看一下,如果我现在要给100个员工发工资,你们的系统能处理吗?"

银行检查了一下,回复:"没问题,100个账户都正常,可以处理。"

你把所有员工的钱先"冻结"在公司的账户里,暂时不转出去。

第二阶段:真正发钱

确认所有人都没问题后,你再次给银行打电话:"好了,现在真正转账吧。"

银行把冻结的钱转到每个员工的账户里,发工资完成。

如果中间出问题

假如有个员工的账户被冻结了,银行告诉你"这个发不了",那你就给银行打电话:"全部取消,钱不发了。"然后该干嘛干嘛去。

这个方案的优缺点

优点 :要么全发,要么全不发,很安全。
缺点:中间等待的时间,公司账户里的钱不能动(被冻结了)。如果100个员工里有一个人手机没信号,银行联系不上,那所有人都得等着,谁也别想先拿到钱。


二、3PC(三阶段提交):改进版的发工资

故事场景

还是发工资那个事,但这次公司觉得上次的方案太磨叽,一个人出问题所有人都得等,想改进一下。

三阶段操作

第一阶段:先打电话确认人在不在

你给每个员工打电话:"喂,你手机有信号吗?能接收转账吗?"

每个员工回复:"有信号,没问题。"

第二阶段:通知准备

你给银行打电话:"把100个员工的钱都先冻结,但别转出去。"

银行执行冻结操作。

第三阶段:正式转账

确认冻结成功后,你再次通知银行:"好了,现在转账吧。"

改进的地方

如果银行在第二阶段之后联系不上你(你手机没电了),银行不会一直傻等,而是等一段时间后自动把钱转出去。这样不会因为一个人掉线,所有人干等着。

这个方案的缺点

虽然是改进了,但万一网络断了一下,有的银行节点收到了"转账"指令,有的没收到,还是会出现有人收到钱有人没收到的情况。所以现实中用得不多。


三、TCC:买火车票的"占座"机制

故事场景

你在12306上买火车票,整个过程需要:

  1. 扣你的钱

  2. 锁定一个座位

  3. 生成一张票

如果这三步中间断了,比如钱扣了但座位没锁上,你就很被动。

TCC的三步

第一步:Try(占座但不付款)

你在APP上选好座位,系统提示:"这个座位给你预留30分钟,请尽快付款。"

此时你的银行卡里还没扣钱,只是被"标记"了一下,这个座位别人也不能买了。

第二步:Confirm(确认付款)

你在30分钟内付了款,系统正式把座位分配给你,生成电子票。

第三步:Cancel(取消占座)

如果你30分钟内没付款,或者主动取消,系统自动释放座位,别人可以继续买。

这个方案的优点

整个过程,你的银行卡里的钱并没有真正被扣,只是被"冻结"了一小会儿。即使中间出了问题,也可以随时取消,不会造成实际损失。而且不需要长时间等待。

适用场景

像订机票、订酒店、抢购商品这种,先"占个坑"再付款的流程,天然适合TCC。


四、本地消息表:记账本+闹钟

故事场景

你在网上买了个手机,下单成功后,系统需要给你发一条确认短信。

但是短信服务有时候会挂,如果因为短信发失败导致整个订单取消,那也太不合理了。

解决方案

第一步:先把重要的事记下来

你下单成功后,系统不仅保存了订单,还在自己的小本本(本地消息表)上写了一行:

"2025年1月1日 10:00,需要给用户xxx发一条下单成功的短信,状态:未发送"

第二步:闹钟定时提醒

系统有一个闹钟,每隔5分钟就去翻这个小本本,找出所有"未发送"的短信任务。

第三步:尝试发送

闹钟叫醒系统后,系统尝试去调用短信服务发送短信。发送成功了,就在小本本上把那行记录改成"已发送"。

第四步:失败了就再试

如果短信服务正好坏了,发送失败,那就保持"未发送"状态,5分钟后闹钟再响,再试一次。

这个方案的特点

  • 短信服务坏了没关系,订单照样成功,只是短信晚一点发。

  • 用本地的小本本(数据库)记录任务,不会丢失。

  • 缺点就是闹钟不停地响,对数据库有点压力。


五、事务消息:高级版的"记账本+闹钟"

故事场景

还是买手机发短信的例子,但这次系统不想自己维护那个"小本本"了,太麻烦,想交给专业的MQ消息队列来做。

解决方案

第一步:先发一个"半成品"消息

系统对MQ(消息队列)说:"我要发一条短信消息,但先别发出去,等我通知。"

MQ收到后,把这个消息存起来,但不真正发送。

第二步:完成本地下单

系统继续完成下单操作,把订单存到数据库。

第三步:正式通知MQ发送

下单成功后,系统告诉MQ:"好了,那条消息可以发了。"

MQ这才把短信真正发出去。

第四步:万一系统挂了怎么办

假设系统在第二步和第三步之间突然断电了,订单已经成功,但忘了告诉MQ"可以发了"。

这时候MQ不会傻等,它会主动打电话回查:"喂,你那个订单到底成功了没有?"

系统回答:"成功了,你发吧。"或者"失败了,把那消息删了吧。"

这个方案的优点

  • 不需要自己维护消息表,MQ帮你做了。

  • 性能更好,吞吐量更高。

  • 需要MQ支持"事务消息"功能,目前RocketMQ支持得比较好。


六、Seata AT模式:一个"透明"的魔法助手

故事场景

前面所有方案都需要你改代码、写额外逻辑,很麻烦。

现在有一个神奇的助手(Seata),你什么都不用改,只需说一句"我要全局事务",它就会自动帮你搞定一切。

魔法助手的工作方式

第一步:自动记录

你在代码里写了一个普通的数据库操作:update account set money = money - 100 where id = 1

助手在你执行之前,偷偷把这条记录原来的样子(比如原来有1000块)记录下来,存到一个专门的"撤销日志"表里。

第二步:执行操作

数据库真的把余额改成了900块。

第三步:如果需要回滚

假如后面的操作失败了,助手会自动读取"撤销日志",生成一条反向SQL:update account set money = 1000 where id = 1,然后执行它。

这样你的数据就恢复到了操作前的样子。

为什么说它是"透明"的

因为你在代码里完全看不到任何分布式事务的痕迹,就像在写普通的本地事务一样。助手在背后悄悄地帮你做了所有事情。

这个方案的代价

助手需要做额外的工作(记录前后快照、加全局锁),所以性能会比TCC差一些,但比2PC好。另外,助手本身需要单独部署一个服务。


总结对比表

方案 生活比喻 一句话特点
2PC 先打电话确认再真正转账 安全但慢,一个人卡住全队等
3PC 多确认一次以防掉线 改进了阻塞问题,但仍有隐患
TCC 火车票占座30分钟 性能好,但需要业务支持"预留"
本地消息表 小本本记任务+闹钟提醒 简单可靠,但闹钟吵得数据库头疼
事务消息 把记账本交给专业快递 性能好,但需要高级MQ支持
Seata AT 自动记录+自动还原 对代码无侵入,最省事

最后几句话

  • 如果你要绝对安全(比如银行转账),数据不能有半点差错,那就忍受慢一点,用2PC或Seata AT。

  • 如果你要高性能(比如秒杀抢购),愿意多写点代码,用TCC。

  • 如果你要异步解耦(比如下单后发通知),用事务消息。

  • 如果你想最简单,不愿意改太多代码,用Seata AT。

没有最好,只有最合适。


希望这6个故事能帮你把分布式事务的几种方案理清楚。如果哪个比喻还不够准确,或者想深入了解某个方案的技术细节,欢迎继续交流。

相关推荐
我只想困告2 小时前
day01-RabbitMQ_2026-05-13
分布式·rabbitmq
cheems95273 小时前
[RabbitMQ] RabbitMQ 工作流程全解析
分布式·rabbitmq
敖正炀4 小时前
读写分离与数据库中间件选型
分布式
Jay-r4 小时前
积极的断舍离:化解时代性焦虑的生活哲学
人工智能·科技·生活·感悟·哲学
Mahir085 小时前
Redis 分布式锁与 Redisson 深度解析:从原生实现到工业级解决方案
数据库·redis·分布式·缓存·面试
敖正炀6 小时前
分布式事务监控与手动恢复平台设计
分布式
逆境不可逃6 小时前
Hello-Agents 第二部分-第四章总结:智能体经典范式构建-包含习题解析和Java版
java·开发语言·javascript·人工智能·分布式·agent
heimeiyingwang6 小时前
【架构实战】RocketMQ实战:分布式消息中间件
分布式·架构·rocketmq
报错小能手6 小时前
分布式讲解—分布式事务解决方案 刚性(2PC、3PC、XA协议)
分布式