先讲个真事。前两年有个做电商的朋友,搞了一套微服务重构,订单、库存、支付全拆开了。上线第一天就出幺蛾子——用户下单成功,库存扣了,结果支付服务超时重试了三回,用户银行卡被扣了三笔钱。他半夜打电话跟我吐槽:“这微服务拆之前好好的,拆完之后钱都对不上了。”这个场景,但凡搞过微服务的人都懂。数据一致性这问题,单体架构时代就是个事务注解的事,到了微服务这儿,变成了绕不过去的坎儿。

坎儿在哪呢?本质上是“本地事务”管不了“跨服务操作”了。以前一个订单流程,在一个数据库里,BEGIN TRANSACTION到COMMIT,要么全成要么全没。现在订单服务、库存服务、支付服务各管各的库,你没法用一个事务把三个库包起来。于是就有了那个经典的“双写”难题:订单库写了,库存库没写成功,咋整?这时候你才发现,分布式系统里“一致性”不是个技术选项,而是个工程取舍。
第一个核心策略,叫“最终一致性+事务消息”。这词听起来玄乎,说白了就是:别指望所有服务同时看到最新数据,但保证一段时间后大家能对齐。具体怎么做?拿订单和库存来说,订单服务先发一条“预扣库存”的消息到消息队列,库存服务消费后扣减,扣完再回一条“已扣减”的消息。如果库存服务挂了,消息就在队列里躺着,等它恢复接着处理。关键点在于,消息的发送和本地事务必须绑定——用本地消息表或者事务消息,保证“订单写库”和“消息发出”这两个动作要么都成要么都不成。有人觉得这不够“即时”,但现实是,绝大多数业务场景,用户根本感知不到那几秒的延迟,而架构的稳定性却因此上了一个台阶。
第二个策略,是“SAGA长事务”。这模式更适合那种流程长、步骤多的业务,比如预订机票+酒店+租车。SAGA的思路是:把一个全局事务拆成一串本地事务,每个本地事务都有对应的补偿操作。比如订机票成功了,订酒店失败了,那就触发“取消机票”的补偿动作,把前面已经做的操作回滚掉。听起来简单,但坑都在细节里。补偿操作本身也可能失败,所以你得给补偿操作设计重试机制,还得考虑幂等性——同一个补偿操作执行两次,效果得一样。我见过不少团队,SAGA流程图画得漂漂亮亮,一上线就发现补偿逻辑里全是隐藏的依赖关系,A服务补偿时要调用B服务,B服务又挂了,整个链条卡死。所以做SAGA,你得有“补偿操作也是核心业务”的觉悟,该加监控加监控,该做演练做演练。
第三个策略,是“本地消息表+定时对账”。这招看起来土,但真管用。做法是,每个服务在自己的数据库里建一张消息表,业务操作和写消息表在同一个本地事务里完成。然后有个定时任务,扫描消息表,把状态是“待发送”的消息发到MQ,发成功的标记为“已发送”。接收方处理完消息后调用发送方的回调接口更新状态。如果消息一直没发出去或者没被消费,定时任务就反复重试。这套机制的妙处在于,它把分布式问题降级成了单机事务问题,每个服务只保证自己的数据是对的,服务之间的协作靠消息表的“状态流转”来驱动。当然,它也有缺点,就是消息表会越积越多,你得定期清理,不然数据库性能会受影响。但对大多数中小团队来说,这方案成本低、易上手、逻辑透明,比那些花里胡哨的分布式事务框架靠谱多了。
说到底,这三个策略不冲突,可以组合用。比如订单主流程用事务消息保证核心链路的一致性,涉及跨多步的复杂流程用SAGA管理补偿,再配一套定时对账兜底,把漏网之鱼捞回来。我见过做得好的团队,不仅技术方案选得对,还配套了“对账平台”,每天凌晨跑一遍全链路数据比对,不一致的自动生成工单,人工介入处理。你看,数据一致性从来不只是技术问题,更是运维和流程问题。
还有个容易被忽略的点:业务设计上要尽量避免分布式事务。比如,别把库存扣减和订单创建放在两个服务里硬凑一个事务,完全可以设计成“订单创建时先锁定库存预留单”,后续再异步确认扣减。很多所谓的一致性难题,其实是业务建模时埋下的雷。你往上游想一步,把服务边界划清楚,把数据归属理明白,很多问题根本不会出现。这也是为什么我一直强调,微服务架构里,最值钱的不是技术框架,而是你对业务的理解深度。
总结一句。微服务架构下的数据一致性,没有银弹,没有一劳永逸的方案。事务消息解决“基本一致性”,SAGA解决“复杂流程回滚”,本地消息表解决“低成本可靠投递”。你真正要做的,是结合自己的业务场景,把这三种策略当成工具箱里的扳手和螺丝刀,该上哪个上哪个,甚至组合使用。别迷信某个框架能帮你搞定一切,也别因为怕出问题就退回单体架构。分布式是一条不归路,但走好了,系统的弹性和扩展性会给你远超预期的回报。关键是,你得有“出了事能追回来”的底气——这才是数据一致性策略存在的真正意义。


