分布式事务的现实挑战
想象一下,你在网上下单买手机,支付成功后却发现库存没扣减,或者订单状态卡在“处理中”。这种情况在单体系统里很少见,但在微服务架构下却成了家常便饭。每个服务管一块数据,支付归支付服务,库存归库存服务,订单归订单服务,一旦中间出点问题,数据就可能对不上。
这就是分布式事务要解决的核心问题:多个服务参与的一个业务操作,要么全部成功,要么全部回滚。架构师的任务,就是在复杂性和一致性之间找到平衡。
常见方案不是万能钥匙
很多人一上来就想用XA协议,觉得两阶段提交(2PC)听起来很稳。但现实是,它太重了。锁资源时间长,一个节点卡住,整个事务都得等。线上系统高并发时,这么搞等于自己给自己挖坑。
TCC(Try-Confirm-Cancel)更灵活一些。比如下单,先Try冻结库存和额度,确认时再真正扣减,如果失败就Cancel释放资源。这就像去餐厅订位,先问“有没有位置”,有就占着,来不了再取消。但TCC对业务侵入大,每个服务都得实现三段逻辑,开发成本高。
最终一致性更接地气
大多数互联网场景其实不需要强一致。用户付完钱,订单状态延迟几秒更新,没关系。这时候用消息队列做最终一致性更合适。
比如订单服务写完本地数据库后,发个消息到MQ,通知库存服务扣减。只要消息不丢,迟早会一致。万一库存服务挂了,重启后还能消费积压消息。这种模式性能好,扩展性强,是很多电商系统的实际选择。
public void createOrder(Order order) {
// 1. 本地事务:保存订单
orderDao.insert(order);
// 2. 发送扣减库存消息
mqProducer.send("decrease_stock", order.getItemId(), order.getCount());
}关键在于保证“本地事务 + 发消息”原子性。可以用事务消息,比如RocketMQ的half message机制,先发个待确认的消息,本地事务成功后再提交确认,失败则回滚。
Saga模式:把大事务拆成小步骤
Saga适合流程长的业务,比如机票预订,涉及航班、酒店、保险多个环节。它把整个流程拆成一系列可补偿的操作。每一步执行成功就往下走,哪步失败就按顺序执行前面各步的补偿动作。
比如订酒店失败了,那就去取消之前已经订好的航班。虽然不能实时回滚,但最终能恢复到初始状态。实现上可以用编排(orchestration)或协同(choreography),前者由一个中心控制器调度,后者靠事件驱动各服务自行响应。
架构师选型时得看业务特点。金融类对一致性要求高的,可以考虑TCC或增强版2PC;电商、社交这类允许短暂不一致的,优先用消息队列+Saga的组合。
别迷信“银弹”,真正的设计是在理解业务的基础上,选最轻量、最可靠的方式把事办成。