为什么“Exactly Once”在工程上是伪命题? 2025-12-24 11:04 ## 一、先给结论(非常直白) > **在分布式系统里,“Exactly Once”不是做不到, > 而是:** > > 👉 **你永远无法同时证明「只执行一次」和「系统没丢数据」。** 所以工程上的真实目标只有两个: - **At-Least-Once + 幂等** - **At-Most-Once + 可接受丢失** “Exactly Once”只存在于 **论文 / 宣传 PPT / 面试题**。 ------ ## 二、为什么大家这么迷恋 Exactly Once? 因为它听起来像: > “系统帮我保证,我不用想异常。” 而现实是: > **异常,才是分布式系统的常态。** ------ ## 三、我们拆一个最常见的“看似 Exactly Once”的流程 ### 场景:MQ 消费 + 数据库更新 ``` 1. 消费消息 2. 更新数据库 3. ACK MQ ``` 你觉得只要这三步按顺序来,就能 Exactly Once,对吧? ------ ## 四、真正的问题在「不可观测的瞬间」 我们来看**每一步之间可能发生什么**。 ------ ### 情况 1:DB 成功,ACK 前宕机 ``` DB ✔ ACK ✘ ``` MQ 视角:**没 ACK → 重投** 业务视角:**已经执行过了** 👉 **重复执行不可避免** 如果你强行“防重复 ACK”,那: - 要么丢消息 - 要么不敢 ACK ------ ### 情况 2:ACK 成功,DB 提交失败 ``` ACK ✔ DB ✘ ``` 👉 **消息永久丢失** 你可能会说:那我先 DB 再 ACK。 看上面情况 1。 ------ ### 情况 3:网络分区(最狠) ``` Consumer 执行成功 网络抖动 Producer / Broker 不确定状态 ``` 这时候: - Broker 不知道你是否成功 - Consumer 也不知道 ACK 是否成功 👉 **“不确定性”本身就击穿 Exactly Once** ------ ## 五、你可能想到的“高级方案”,为什么也不成立? ### 1️⃣ 分布式事务(XA / 两阶段提交) 看起来很美: ``` Prepare Commit ``` 现实: - 性能崩 - 锁时间极长 - 任一节点超时,全链路阻塞 - MQ 厂商基本不推荐 👉 **工程上基本等于自杀** ------ ### 2️⃣ MQ 官方的 Exactly Once 承诺? 比如: - Kafka EOS - RocketMQ 事务消息 **注意它们的真实含义:** > **Exactly Once ≠ 业务只执行一次** 它们保证的是: - 同一条消息 - 在同一个 Topic + Partition - 不重复写入日志 但: - **不管你 DB** - **不管你业务副作用** - **不管第三方接口** 👉 业务层面仍然是 **At-Least-Once** ------ ### 3️⃣ “我用唯一索引,不就 Exactly Once 了吗?” 这也是一个**非常经典的误解**。 ``` UNIQUE(order_no) ``` 确实保证: - 数据只插一次 但没保证: - 第三方接口只调一次 - 钱只扣一次 - 短信只发一次 👉 **Exactly Once ≠ 数据不重复** ------ ## 六、工程界的真实共识是什么? > **放弃 Exactly Once,转而设计“可重复执行的系统”** 这句话你记住,基本不会再走偏。 ------ ## 七、工程上的“正确姿势”是什么? ### 1️⃣ 语义选择,而不是执念 | 语义 | 使用场景 | | ------------- | ---------------- | | At-Most-Once | 日志、埋点、监控 | | At-Least-Once | 订单、支付、库存 | | Exactly Once | PPT / 面试 | ------ ### 2️⃣ 幂等 + 状态机 > Exactly Once ``` if (order.status == PAID) { return; } doPay(); order.status = PAID; ``` 你不是保证“只执行一次”, 你是在保证: > **执行多次,结果一致** ------ ## 八、回到你一开始那个 BIGINT(20) 你现在应该能感受到那条“狠话”真正的意思了: > **纠结 BIGINT(20) 的人, > 本质上是在寻找“看起来像约束”的东西, > 而不是直面“不确定性”。** Exactly Once BIGINT(20) `@Transactional + MQ` 它们本质是同一类心理依赖。 ------ ## 九、一句老工程师送你的话(真心) **分布式系统不是靠“保证”跑起来的,是靠“兜底”活下去的。** --- 以上对话来自ChatGPT --END--
发表评论