上周和一位老朋友吃饭,他正焦头烂额。他们团队刚把单体应用拆成微服务,业务跑得飞快,但一到月底对账,财务数据总是对不上。他苦笑着说:“现在系统里最一致的,可能就是‘不一致’本身了。”
我太懂这种感觉了。微服务带来了敏捷和弹性,却也把原本在数据库里一个事务就能搞定的事情,拆得七零八落。订单服务扣款成功了,库存服务却因为网络抖动没减库存;积分服务发放了奖励,主业务却因为异常回滚了。
数据一致性,成了微服务架构下最让人头疼的“房间里的大象”。
为什么“最终一致”听起来很美,做起来很痛?
很多文章会告诉你,拥抱“最终一致性”吧,这是分布式系统的常态。道理没错,但“最终”是多久?一分钟?一小时?还是一天?
对于用户来说,支付成功后订单状态还是“待支付”,这“最终”的几秒钟就是糟糕的体验。对于财务系统,这“最终”的几小时可能意味着严重的对账差异。
所以,我们真正要解决的,不是“要不要一致性”,而是“在什么场景下,需要什么级别的一致性”。
别急着上“大杀器”:先试试这些轻量级方案
坦白讲,一提到分布式事务,很多人脑子里蹦出的就是两阶段提交(2PC)、三阶段提交(3PC)。它们确实是标准答案,但也是重量级武器,复杂、性能损耗大,在很多场景下属于“杀鸡用牛刀”。
在实际项目中,我通常会先按顺序考虑下面这几招,它们能解决80%的问题:
本地消息表:这是我最喜欢、也最实用的模式之一。核心思想是“靠山山倒,靠人人跑,不如靠自己”。在执行业务操作的同时,在本地数据库插入一条消息记录,然后有一个后台任务异步去推动其他服务的操作。即使推送失败,任务会重试,直到成功。它保证了消息的可靠投递,实现了最终一致。
- 优点:简单,与业务逻辑耦合低,不需要额外中间件。
- 缺点:消息表会带来数据库压力,且是异步的,不适合强实时场景。
可靠事件模式:可以看作是本地消息表的“升级版”,引入了消息队列(如RocketMQ、Kafka)。业务服务发布事件到MQ,其他服务订阅并消费。关键在于,生产者和MQ之间、消费者处理业务和确认消费之间,需要保证本地事务和消息操作的原子性(这就是RocketMQ事务消息解决的问题)。
- 优点:解耦更彻底,性能更好。
- 缺点:引入了消息队列的复杂度,需要处理消息幂等(同一条消息不能重复处理业务)。
补偿事务(TCC):Try-Confirm-Cancel。这要求每个服务都实现三个接口。以转账为例:
- Try:冻结A账户的100元,冻结B账户的+100元额度(预留资源)。
- Confirm:真正扣减A的100元,增加B的100元(使用预留的资源)。
- Cancel:释放冻结的额度(回滚)。
- 优点:避免了长事务锁资源,性能较好。
- 缺点:业务侵入性强,每个服务都要改造成三个操作,设计复杂。
当轻量级搞不定时:认识一下Saga模式
对于跨多个服务、执行时间很长的业务流程(比如一个旅行预订,涉及机票、酒店、租车),上面的方案可能就不太合适了。这时候,Saga模式登场了。
Saga的核心思想是:把一个长事务拆分成一系列本地小事务,每个小事务都有对应的补偿操作。然后通过一个“协调器”来按顺序执行它们。如果中途某个步骤失败,就反向执行前面所有步骤的补偿操作,完成回滚。
它有两种实现方式:
- 编排式:有一个中心大脑(协调器)指挥每个服务干什么。服务之间不直接通信。结构清晰,但协调器容易变成单点并承载过多逻辑。
- 协同式:事件驱动。每个服务执行完后,发布一个事件,触发下一个服务执行。如果失败,则发布一个失败事件,触发前面的服务进行补偿。更松耦合,但流程分散在各地,难以追踪。
Saga不保证隔离性,这意味着在它执行过程中,其他事务可能看到中间状态。这需要业务上能够接受,或者通过一些设计(如预留字段)来规避。
我的实践心得:没有银弹,只有权衡
工作这些年,我最大的体会是:分布式事务的选择,本质上是一种权衡。 在一致性、可用性、性能、复杂度和开发成本之间找平衡点。
- 强一致性场景少之又少:仔细审视你的业务,真的需要瞬间一致吗?大部分时候,用户对“稍等片刻”的容忍度比我们想象的高。
- 能串行就别并行:如果业务允许,将分布式调用改为串行,能极大地简化问题。虽然损失一点性能,但换来了简单和可靠。
- 监控和可观测性比事务本身更重要:再好的方案也可能出错。必须要有完善的日志、链路追踪和业务监控。当不一致发生时,能快速定位、修复和补偿,这有时比预防更实际。
- 从业务边界开始设计:尽量让需要强一致性的操作落在同一个服务内。如果“订单”和“库存”总是要一起变动,那它们或许本就不该被拆成两个服务。DDD中的聚合根概念在这里非常有指导意义。
写在最后
回到我朋友的那个问题。后来我们帮他分析,发现大部分不一致都源于非核心的积分、优惠券发放。他们最终采用了“可靠事件+对账补偿”的策略:核心支付链路保证强一致,周边系统通过消息队列异步处理,并每天定时对账,对不上的少数情况自动发起补偿。
系统稳定了,他的头发也保住了一些。
分布式事务是一个深水区,但别怕。从理解业务真实需求开始,选择最适合而不是最时髦的方案。记住,好的架构不是没有问题的架构,而是问题发生时,你能清晰知道它在哪里,并且能快速解决的架构。
你目前在微服务数据一致性上,遇到最棘手的挑战是什么呢?