0. 先讲个让人睡不着的故事
双十一凌晨 02:17,客服群置顶三条消息:
用户A:提示支付成功,订单显示「待支付」,库存却扣了,我还能不能买? 用户B:余额扣了 199,积分没到,客服说「最终会一致」,等到什么时候? 用户C:转账显示成功,对方没收到,你们是不是把钱吃了?值班开发小王把三条链路日志拉出来,发现一个共同点:
| 链路 | A 侧结果 | B 侧结果 | 用户体感 |
|---|---|---|---|
| 下单扣库存 | 库存库:扣减成功 | 订单库:插入超时回滚 | 有扣无单 |
| 支付加积分 | 账户库:扣款成功 | 积分库:MQ 丢了 | 有扣无分 |
| 跨行转账 | A 账户:减成功 | B 账户:加失败 | 单边账 |
每条链路里,两边各自的本地事务都「成功/失败得清清楚楚」,合在一起却是「全局灾难」。
组长老张甩过来一句后来成了本专栏 slogan 的话:
「局部 ACID 很成功,全局一致性很失败。」
本系列,就是把这句话背后的整套知识体系,拆成能啃、能写代码、能面试、能避坑的七篇文章。
1. 本专栏贯穿案例:极简购电商
为了避免每篇换故事,我们固定一家虚构公司:
极简购(MiniMall)—— 把单体拆成微服务后的典型互联网电商。
核心业务不变量:
- 有订单 ⇒ 必须有对应库存占用(或已释放)。
- 支付成功 ⇒ 账户扣款与订单状态必须对齐。
- 扣款成功 ⇒ 积分最终必须到账(允许延迟,不允许永久丢失)。
- 转账:A 减 + B 加,不允许长期单边。
关键人物:
- 小王:1~3 年经验,爱写
@Transactional,以为加了注解就天下太平。 - 老张:十年架构,说话阴阳怪气但特准。
- 产品经理小美:永远问「能不能保证实时一致」。
- DBA 老周:一听 XA、长事务就血压升高。
2. 专栏目录
| 篇号 | 标题 | 你吃透什么 | 核心案例 |
|---|---|---|---|
| 01 | ACID 为什么管不到微服务 | 本地事务边界、undo/redo、单边账复现 | 同库转账 vs 跨库转账 |
| 02 | CAP / PACELC / BASE | 为什么必须妥协、最终一致如何落地 | 分区演练、支付中状态机 |
| 03 | 2PC / 3PC 图解吃透 | 强一致协议代价、为何微服务少用 | XA 转账、协调者宕机 |
| 04 | TCC 实战:冻结、空回滚、悬挂 | 业务补偿三板斧与异常全集 | 下单预扣库存完整代码 |
| 05 | 本地消息表与事务消息 | Outbox、半消息、回查、幂等 | 下单通知积分 |
| 06 | Seata AT 深度:镜像与 undo | 一阶段提交、二阶段补偿、脏写校验 | 订单+库存 AT 落地 |
| 07 | 选型与全链路复盘 | 一张表定生死、避坑清单 | 极简购全链路改造 |
3. 一张总图:你要走的路
演进一句话:
2PC 正确但太慢太脆 → 3PC 想去阻塞却难落地 → TCC 把锁变成冻结 → 消息把跨服务改成可靠事件 → Seata 把补偿自动化。
4. 读系列前的三个防骗提醒
- 分布式事务不是银弹。能收口单库单事务,永远优先收口。
- 「最终一致」没有闭环 = 永久不一致。闭环 = 可靠投递 + 幂等 + 补偿 + 对账。
- ACID 的 C ≠ CAP 的 C。混为一谈,面试直接掉段,生产更会选错方案。
下期预告
《01|ACID 为什么管不到微服务:用一次「同库转账 vs 跨库转账」把地基砸透》
我们会用可运行的 SQL/Java 片段,亲手制造一笔「单边账」,再讲清楚 undo/redo、隔离级别和「一致性到底是谁的责任」。