在这里插入图片描述
在单体应用时代(比如一个巨大的 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):
  1. 扣你 100 块。
  2. 在自己的小本本(本地消息表)上记一笔:“待通知朋友银行 +100”。
  3. 这两步在一个本地事务里,绝对安全
  • 发信 (MQ):

  • 只要小本本上有记录,就一直给 MQ 发消息。

  • 朋友银行 (B):

  • 收到 MQ 消息,给朋友加 100 块。

  • 如果 B 挂了?没关系,MQ 会一直重试,直到 B 活过来。

  • 如果 B 处理过了?没关系,做个幂等性校验(查重),不再加钱。

  • 结果: 虽然中间可能有几秒钟延迟,但最终这 100 块钱一定会转过去。这就是最终一致性

更多推荐