1. 分布式事务概述:为什么库存系统需要分布式事务、CAP理论与BASE理论、分布式事务的核心挑战
大家好,我是老张。今天咱们聊聊分布式事务。
说实话,我做了十几年后端,踩过最多的坑就是分布式事务。尤其是库存系统,那真是「一着不慎,满盘皆输」。你想想看,一个订单下来,扣库存、减优惠券、加积分、通知物流……这些操作分散在不同的服务里,任何一个环节出问题,数据就乱套了。
嗯,咱们先从最基础的问题说起。
为什么库存系统需要分布式事务?
先问个问题:一个单体应用,所有数据都在一个库里,事务好办吧?begin transaction 一把梭,要么全成功,要么全回滚。但到了微服务时代,库存系统被拆成了独立的服务,订单服务、支付服务、库存服务各管各的数据库。
举个例子:
- 用户下单,订单服务创建订单
- 库存服务扣减库存
- 支付服务扣款
如果库存扣了,但支付失败了怎么办?库存已经减了,用户却付不了钱,这库存就「凭空消失」了。我在项目中遇到过类似情况,那次是双十一大促,库存对不上账,财务追着我问了一周……
说白了,分布式事务就是用来解决「跨服务、跨数据库」的数据一致性问题。库存系统尤其敏感,因为库存是钱,一分一毫都不能差。
CAP理论与BASE理论
聊分布式事务,绕不开 CAP 理论。这个理论是 Eric Brewer 在 2000 年提出的,简单说就是:
- C(Consistency)一致性: 所有节点看到的数据是一样的
- A(Availability)可用性: 每个请求都能得到响应(不保证数据是最新的)
- P(Partition Tolerance)分区容错性: 系统允许网络分区,节点之间可能通信失败
CAP 告诉我们:三者最多只能同时满足两个。你想想看,网络分区是必然发生的,所以 P 必须选。那剩下的就是在 C 和 A 之间做取舍。
| 组合 | 典型场景 | 例子 |
|---|---|---|
| CP(一致性+分区容错) | 对数据一致性要求极高 | 银行转账、库存扣减 |
| AP(可用性+分区容错) | 对用户体验要求高 | 社交动态、商品浏览 |
库存系统属于典型的 CP 场景。库存数据必须准确,不能出现「超卖」或「少卖」。但完全追求一致性,又会影响可用性——比如网络分区时,你可能要等节点恢复才能继续扣库存。
这就引出了 BASE 理论。
我个人的习惯是:核心链路用强一致性,非核心链路用最终一致性。比如库存扣减必须强一致,但日志记录可以异步写,最终一致就行。
分布式事务的核心挑战
好,理论讲完了,咱们聊聊实战中会遇到哪些坑。
1. 网络不可靠
这是最头疼的。你发一个「扣库存」的请求,对方可能收到了,也可能没收到。更麻烦的是:对方收到了也处理了,但响应丢了。这时候你怎么办?重试?万一重复扣了呢?
我曾经在项目中遇到过:因为网络抖动,库存服务收到了两次扣减请求,结果库存多扣了。从那以后,我所有的接口都加了幂等性校验。
2. 事务的原子性难以保证
在单体应用里,事务要么全成功要么全回滚。但在分布式环境下,A 服务成功了,B 服务失败了,怎么让 A 回滚?这就涉及到「补偿机制」——比如发一条消息让 A 把刚才的操作撤销。
但补偿操作本身也可能失败……嗯,这就是分布式事务的「套娃」问题。
3. 数据一致性模型的选择
强一致性还是最终一致性?这是个灵魂拷问。强一致性实现复杂(比如 2PC、3PC),性能也差。最终一致性实现简单,但业务上要能接受短暂的不一致。
我建议:库存扣减用强一致性,库存查询可以用最终一致性。毕竟用户查库存时,差几毫秒的误差是可以接受的。
4. 性能开销
分布式事务的协调、锁、日志、补偿……每一步都有性能开销。尤其是 2PC,第一阶段锁资源,第二阶段才释放,并发高的时候很容易死锁。
我记得有个项目,上线第一天就死锁了。排查下来,是分布式事务的锁超时时间设得太短,导致大量回滚。后来我把超时时间调长了,又加了重试机制,才算稳住。
本章知识体系
下面这张图,是我自己画的,帮你理清本章的核心逻辑:
这张图把本章的核心内容串起来了。你仔细看,左边是「为什么需要」,中间是「理论支撑」,右边是「核心挑战」。三者缺一不可。
好了,这一章就到这里。分布式事务不是一蹴而就的,需要慢慢理解。下一章咱们会深入具体的实现方案,比如 2PC、TCC、Saga 这些,到时候再细聊。