引言:一个转账引发的思考
想象一个场景:你在电商平台下单购买商品,系统需要同时完成三件事:
-
订单服务创建订单
-
库存服务扣减库存
-
账户服务扣减余额
如果订单创建成功了,库存也扣减了,但账户扣款失败了怎么办?如果每个服务都有自己的数据库,我们该如何保证这三个操作要么全部成功,要么全部失败?
这就是分布式事务要解决的核心问题。今天,我们就来深入探讨这个让无数工程师头疼却又不得不面对的话题。
一、为什么需要分布式事务?
1.1 单体应用的美好时光
在单体应用时代,事务很简单。我们有本地事务(Local Transaction),依靠数据库的 ACID 特性就能搞定一切:
@Transactional
public void createOrder(Order order) {
// 所有操作在同一个数据库事务中
orderMapper.insert(order);
inventoryMapper.decrease(order.getProductId(), order.getQuantity());
accountMapper.decrease(order.getUserId(), order.getAmount());
// 任何一步失败,自动回滚
}
简单、可靠、优雅。但当我们拆分成微服务后,这一切都变了。
1.2 微服务时代的挑战
微服务架构带来了:
-
数据库分散:每个服务有自己的数据库,无法使用本地事务
-
网络不可靠:服务间通过网络通信,可能超时、丢包
-
独立部署:各服务独立部署,可能某个服务宕机
-
异构系统:可能有 MySQL、Redis、MongoDB 等多种数据源
这就是分布式事务的根源:跨越多个网络调用的操作,如何保证原子性?
二、理论基础:CAP 与 BASE
2.1 CAP 定理的无奈
CAP 定理告诉我们,在分布式系统中,一致性(Consistency)、可用性(Availability)、分区容错性(Partition Tolerance)三者只能同时满足两个。
-
C(一致性):所有节点同一时间看到相同数据
-
A(可用性):系统一直可以响应请求
-
P(分区容错性):网络分区时系统仍能工作
由于网络分区是客观存在的(你无法消除网络故障),所以实际上我们只能在 CP 和 AP 之间选择。
举个例子:
-
CP 系统(如 Zookeeper):宁可拒绝服务,也要保证数据一致
-
AP 系统(如 Cassandra):宁可返回旧数据,也要保证服务可用
2.2 BASE 理论的妥协
既然 CAP 无法同时满足,我们就用 BASE 理论来指导分布式事务设计:
-
BA(Basically Available):基本可用,允许部分不可用
-
S(Soft State):软状态,允许数据存在中间状态
-
E(Eventually Consistent):最终一致性,不要求实时一致
这就是分布式事务的核心思想:放弃强一致性,追求最终一致性。
三、分布式事务解决方案全景
3.1 两阶段提交(2PC):理想主义者的方案
原理
2PC 是最经典的分布式事务方案,包含两个阶段:
阶段一:准备阶段(Prepare)
协调者:大家能否执行这个事务?
参与者A:我准备好了,资源已锁定
参与者B:我准备好了,资源已锁定
参与者C:我准备好了,资源已锁定
阶段二:提交阶段(Commit)
协调者:所有人都准备好了,提交!
参与者A:提交成功
参与者B:提交成功
参与者C:提交成功
核心代码示例
// 协调者
public class TransactionCoordinator {
private List<Participant> participants;
public boolean executeTransaction() {
String txId = generateTxId();
// 阶段一:准备
List<Boolean> prepareResults = new ArrayList<>();
for (Participant participant : participants) {
boolean prepared = participant.prepare(txId);
prepareResults.add(prepared);
}
// 如果所有参与者都准备好了
if (prepareResults.stream().allMatch(r -> r)) {
// 阶段二:提交
for (Participant participant : participants) {
participant.commit(txId);
}
return true;
} else {
// 有人没准备好,全部回滚
for (Participant participant : participants) {
participant.rollback(txId);
}
return false;
}
}
}
致命缺陷
-
同步阻塞:所有参与者在准备阶段会锁定资源,等待协调者指令
-
单点故障:协调者宕机,所有参与者一直阻塞
-
数据不一致:网络分区时,部分参与者可能提交,部分回滚
-
性能问题:多次网络往返,吞吐量低
现实情况:2PC 在生产环境很少使用,只在数据库集群等特定场景中应用(如 MySQL 的 XA 事务)。
3.2 三阶段提交(3PC):改进但仍不完美
3PC 在 2PC 基础上增加了超时机制和预提交阶段,缓解了阻塞问题,但仍然无法完全解决一致性问题。由于复杂性高、实用性低,这里不展开讲解。
3.3 TCC(Try-Confirm-Cancel):务实主义者的选择
原理
TCC 是一种应用层的两阶段提交,它将业务逻辑分为三个阶段:
-
Try:尝试执行,预留资源
-
Confirm:确认执行,真正执行业务
-
Cancel:取消执行,释放资源
关键区别:TCC 不依赖数据库事务,而是通过业务逻辑来保证一致性。
实战案例:电商下单
// 订单服务
public class OrderService {
// Try:创建待确认订单
public void tryCreateOrder(OrderDTO orde

最低0.47元/天 解锁文章
5355

被折叠的 条评论
为什么被折叠?



