微服务最大的坑:A 扣钱了 B 报错,钱去哪了?(分布式事务详解)

在单体应用时代(比如一个巨大的 Java 程序连一个 MySQL),事务很简单。你对数据库喊一声:“要么全做,要么全不做!”(ACID),数据库就会乖乖听话。
但是到了微服务时代,事情变得失控了。
- 订单服务在机器 A,连着数据库 A。
- 库存服务在机器 B,连着数据库 B。
- 支付服务在机器 C,连着数据库 C。
你想实现:“下订单 -> 扣库存 -> 扣款”。
如果“下订单”成功了,“扣库存”成功了,但“扣款”时网络断了……
完蛋!用户没付钱,库存却少了,或者库存没少,钱却扣了。 这就是分布式事务要解决的“数据一致性”问题。
💻 一、技术分析:两大流派
解决这个问题,主要有两派思路:刚性事务 (2PC) 和 柔性事务 (TCC/最终一致性)。
1. 2PC (两阶段提交) —— 强一致性
- 逻辑: 引入一个**“大管家” (事务协调者)**。
- 第一阶段 (Prepare): 管家问所有人:“准备好了吗?” 所有人锁住资源,回复“Yes”。
- 第二阶段 (Commit): 管家看大家都 Yes,喊:“提交!” 大家一起提交。
- 缺点: 太慢了! 在第一阶段,所有人都得锁住数据库,等着管家发话。只要有一个人卡住,全员陪跑。高并发下这就是灾难。
2. TCC (Try-Confirm-Cancel) —— 最终一致性
- 逻辑: 不锁资源,而是**“先占座”**。
- Try: 尝试扣款(冻结资金)。
- Confirm: 真的扣款(解冻并扣除)。
- Cancel: 出事了,把冻结的钱退回去(解冻)。
- 优点: 并发高。
- 缺点: 代码量爆炸。每个接口都要写三个逻辑(正常、确认、回滚)。
3. 本地消息表 (最终一致性) —— 最实用
- 逻辑: 利用消息队列 (MQ)。
- A 做完了,发条消息给 MQ:“我搞定了,B 你接着做”。
- A 保证消息一定发出去,B 保证消息一定收到并执行(如果不成功就重试)。
🕶️ 二、故事场景:黑帮的联合抢劫行动
为了搞懂最经典的 2PC (两阶段提交) 和 TCC,我们将 分布式事务 比作一次 “黑帮联合抢劫行动”。
- 老大 (Coordinator): 事务管理器 ™。
- 小弟 A (订单): 负责开锁。
- 小弟 B (库存): 负责装钱。
剧本一:2PC (两阶段提交) —— 严谨但笨重
-
第一阶段 (Prepare - 举枪)
-
老大喊:“所有人举枪,准备!”
-
小弟 A 举枪瞄准(锁住资源),喊:“就位!”
-
小弟 B 举枪瞄准(锁住资源),喊:“就位!”
-
关键: 这时候大家都举着枪不敢动(阻塞),等着老大下一条命令。
-
第二阶段 (Commit - 开火)
-
老大看大家都就位了,吼道:“动手!”
-
小弟 A 开锁(提交事务)。
-
小弟 B 装钱(提交事务)。
-
行动成功。
-
意外情况 (Rollback)
-
如果第一阶段小弟 B 喊:“枪卡壳了!”(报错)。
-
老大立马吼:“取消行动!所有人撤退!”
-
小弟 A 虽然已经瞄准了,但也得把枪收回来(回滚)。
剧本二:TCC (Try-Confirm-Cancel) —— 灵活但麻烦
这次黑帮变聪明了,觉得举着枪等人太傻(性能差),决定分头行动。
-
Try (预留资源)
-
老大说:“今晚行动。A,你去配把钥匙;B,你去买个蛇皮袋。”
-
小弟 A:配了把钥匙,放在口袋里(冻结资源)。
-
小弟 B:买了袋子,放在车里(冻结资源)。
-
关键: 此时还没真正动手,只是准备工作。
-
Confirm (确认执行)
-
老大看准备好了,发短信:“动手!”
-
小弟 A:用钥匙开门。
-
小弟 B:用袋子装钱。
-
Cancel (业务补偿/回滚)
-
意外: 老大发现警察来了。
-
老大发短信:“风紧扯呼!A 把钥匙扔了,B 把袋子退了!”
-
小弟 A:扔掉钥匙(解冻资源)。
-
小弟 B:退掉袋子(解冻资源)。
-
关键: 这需要小弟们自己通过“扔钥匙”、“退袋子”的行为来抵消之前的影响。
🚀 三、进阶:MQ + 本地消息表(最大努力通知)
这是互联网大厂最常用的方案,比如支付宝转账。
- 场景: 你给朋友转 100 块。
- 你的银行 (A):
- 扣你 100 块。
- 在自己的小本本(本地消息表)上记一笔:“待通知朋友银行 +100”。
- 这两步在一个本地事务里,绝对安全。
-
发信 (MQ):
-
只要小本本上有记录,就一直给 MQ 发消息。
-
朋友银行 (B):
-
收到 MQ 消息,给朋友加 100 块。
-
如果 B 挂了?没关系,MQ 会一直重试,直到 B 活过来。
-
如果 B 处理过了?没关系,做个幂等性校验(查重),不再加钱。
-
结果: 虽然中间可能有几秒钟延迟,但最终这 100 块钱一定会转过去。这就是最终一致性。
更多推荐
所有评论(0)