☰
【分布式事务连载 02】CAP、PACELC 与 BASE:老板要实时一致,架构师为何说不
2026/9/30 11:19:31 网站建设 项目流程

【分布式事务连载 02】CAP、PACELC 与 BASE:老板要实时一致,架构师为何说不

系列第 2 篇|承接:单边账说明「本地 ACID 不够」|本篇回答:那我们到底能承诺多强的一致?
目标:用分区演练 + 订单状态机,吃透 CAP / PACELC / BASE,并把「最终一致」落到可执行闭环。


1. 问题引入:产品经理小美的一句话

事故复盘会上,小美拍桌子:

「能不能保证:用户点完支付,立刻、永远、两边绝对一致?做不到就别上微服务。」

老张喝了口凉水:

「能。把所有服务塞回一个库,关掉跨机房,别拆。
你要跨服务、跨机房、高可用,还要『立刻永远绝对』——物理定律先不同意。」

本篇不站队吵架,只把定律讲清楚,再讲工程怎么「认怂认得漂亮」。


2. CAP:三个字母,真正难的是分区发生时

2.1 定义用人话再说一遍

CAP由 Eric Brewer 在 2000 年提出,指的是分布式系统的三个核心特性:

字母含义用户体感
C Consistency(一致性)所有节点同一时刻读到同一份最新数据(强一致 / 线性一致视角)「我在哪读都一样」
A Availability(可用性)非故障节点必须在有限时间给出成功/失败响应,不能无限卡住「系统还活着,能点」
P Partition tolerance(分区容错性)节点之间网络断了,系统仍按设计继续运行「机房光纤被挖断也有预案」

保C弃A

保A弃C

必须容忍分区 P

分区发生时

拒绝写入/读取 - 系统繁忙

继续本地服务 - 短暂不一致

核心结论:在网络分区(P)必然发生的前提下,只能在 C 和 A 之间二选一。

  • CP 系统:牺牲可用性,保证一致性。分区时拒绝服务,等恢复后再响应。例如:ZooKeeper、HBase、etcd。
  • AP 系统:牺牲一致性,保证可用性。分区时各节点继续响应,但可能返回旧数据。例如:Cassandra、DynamoDB、Eureka。

注意:CAP 里的 C 是线性一致性(强一致),不是数据库 ACID 里的 C。ACID 的 C 是“别把数据搞坏、别违反业务规则”,CAP 的 C 是“所有节点看到的数据要一样”。

2.2 分区演练:极简购两个机房

假设订单服务主写在机房 1,只读副本在机房 2;光纤被挖断:

ReplicaDC2PrimaryDC1UserDC2UserDC1ReplicaDC2PrimaryDC1UserDC2UserDC1network partitionalt[choose CP][choose AP]pay success order=PAIDquery ordererror busy keep correctstill UNPAID available but stale
  • CP 阵营例子:不少强一致元数据、传统 XA/2PC 协调思路——宁可失败,不给错数据。
  • AP 阵营例子:DNS、很多缓存、最终一致订单查询——先让你查,稍后对齐。

2.3 和分布式事务的关系

事务思路更偏代价
2PC / XACP阻塞、延迟、吞吐差
消息最终一致 / TCC 补偿AP + 事后收敛中间态、要幂等对账
Seata AT更偏最终一致一阶段已提交,存在可见中间态

宣称「我们系统同时满足 CAP」——面试口径直接判错;生产里通常是CP 或 AP 为主,再局部补强。


3. PACELC:没分区时,你也在做选择

CAP 只描述「分区时」。生产还有半句更扎心的PACELC:

PACELC由 Daniel Abadi 在 2012 年提出,是对 CAP 的补充和细化。它指出:CAP 只讨论了分区发生时的取舍,但没有分区(正常运行时)也有取舍——那就是延迟(Latency)和一致性(Consistency)之间的权衡。

若有Partition:在A与C间选;
Else(没分区):在Latency 与Consistency 间选。

P 发生 → 选 A 或 C P 不发生 → 选 L(低延迟)或 C(强一致)

极简购例子:

场景更偏为什么
收银台同步扣库存再返回C,延迟高用户要「买到」的确定感
支付成功后异步加积分L,短暂不一致积分晚 3 秒可接受
跨城同步 2PC 转账C,延迟更高金融不变量
日志/埋点收集L丢一点也能活

小美要的「立刻」,有时是L 与 C 的冲突,甚至还没到分区。把所有写都做成同步强一致,双十一 QPS 会先把你打穿。


4. BASE:不是放弃一致,是把一致推迟到可接受窗口

4.1 三个词怎么落到订单

BASE含义极简购落地
BABasically Available故障时降级仍提供核心能力积分服务挂了,下单支付仍可完成
SSoft state允许中间态订单:WAIT_PAY→PAYING→PAID/CLOSED
EEventually consistent无新更新后最终收敛支付回调 + 超时关单 + 日终对账

重要:BASE不否定ACID。单库内照样 ACID;跨服务用 BASE 谈「全局怎么收敛」。

4.2 订单状态机

create order

start pay

pay callback ok

pay failed retry

timeout

pay timeout close

WAIT_PAY

PAYING

PAID

CLOSED

PAYING 是软状态窗口:库存预扣中、余额可能冻结,必须有超时与补偿。

对应动作(没有这些就不是 BASE,是「烂尾」):

状态/事件必须做什么
进入PAYING库存 Try 预扣 / 超时时钟启动
PAIDConfirm 扣减;发「支付成功」可靠事件
CLOSEDCancel 释放预扣;若已扣款则退款补偿
日终订单 vs 支付单 vs 库存流水对账

4.3 最终一定一致怎么证明?

发现差异

对齐

业务写入

可靠投递 Outbox

幂等消费

失败可补偿

定时对账兜底

收敛完成

窗口多长算合格?业务定义,不是技术口号。

业务可接受窗口兜底
支付结果展示秒级主动查单 + 回调
积分到账秒~分钟重试 + 对账补发
跨行清算小时~日终清算文件对账
发票开具小时级人工 + 批次任务

5. 实战案例:支付中状态 + 超时关单

5.1 表字段

CREATETABLEtrade_order(idBIGINTPRIMARYKEY,statusVARCHAR(16)NOTNULL,-- WAIT_PAY / PAYING / PAID / CLOSEDpay_deadlineDATETIMENOTNULL,-- 超时关单时间versionINTNOTNULL,-- 乐观锁...);

5.2 发起支付:进入软状态

@TransactionalpublicvoidstartPay(LongorderId){TradeOrderorder=orderMapper.selectForUpdate(orderId);if(order.getStatus()!=WAIT_PAY){thrownewBizException("状态不允许支付");}orderMapper.updateStatus(orderId,WAIT_PAY,PAYING);// 同时:库存 Try、启动超时任务(或依赖 pay_deadline 扫描)stockClient.tryFreeze(order.getSkuId(),order.getCount(),orderId);}

5.3 超时关单任务防止预扣永久占用

@Scheduled(fixedDelay=5000)publicvoidcloseExpiredPayingOrders(){List<TradeOrder>list=orderMapper.selectExpired(PAYING,LocalDateTime.now());for(TradeOrdero:list){// 幂等关单intn=orderMapper.casUpdateStatus(o.getId(),PAYING,CLOSED,o.getVersion());if(n==1){stockClient.cancelFreeze(o.getId());// Cancel 必须幂等// 若支付渠道已成功,走对账纠偏,而不是盲关}}}

5.4 对账兜底

每日 02:00: 支付中心成功流水 LEFT JOIN 订单 - 有支付无 PAID 订单 → 补成 PAID + Confirm 库存 - 有 PAID 无支付 → 告警人工(或自动退款流程) - 库存冻结超时未释放 → 强制 Cancel + 告警

没有这步,你的「最终一致」只是希望;有了这步,才是工程。


6. 把三个理论嵌回极简购选型语言

是

否可对账

是

否

是

资损风险且窗口必须接近0?

偏 CP: XA/强同步

偏 AP+BASE

多方资源预留?

TCC / SAGA

Outbox / 事务消息

低侵入多库写?

Seata AT

下篇起我们进入具体协议与方案;你心里要有这把尺子:先问不变量与窗口,再问用什么中间件。


7. 踩坑总结

  1. 别用「最终一致」偷换 CAP 的 C。最终一致是 BASE 语境,不是「我们也有 C」。
  2. 中间态不设超时 = 资源泄漏(库存预扣成永久冻结)。
  3. 只异步、不幂等、不对账 = 永久不一致。
  4. 无分区时也有 L vs C:同步链路堆太多,双十一先死在延迟上。
  5. 对用户要诚实:积分「预计 5 分钟内到账」比「已到账」却查不到,体验更好,客服更少。

自测题

  1. 分区时保 C 弃 A,用户会看到什么?保 A 弃 C 呢?
  2. PACELC 比 CAP 多解释了什么生产现象?
  3. 画出你司一个订单状态机,标出每个中间态的超时与补偿动作。

下期预告

《03|2PC / 3PC:最像正经事务的方案,为何在微服务里经常变成堵车?》

我们会用「工行→建行」转账动画式时序图,把 Prepare/Commit、协调者宕机、3PC 超时误判一个个拆开。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询