做电商系统时,下单、扣库存、发优惠券往往分散在不同服务里。一个请求打过来,三个操作得一起成功,否则全得回滚。这时候单靠数据库事务早就撑不住了,分布式事务就成了绕不开的坎。
为什么传统事务不灵了?
以前所有逻辑都在一个应用里,用 Spring 的 @Transactional 注解就能搞定。可现在订单服务调库存服务要走网络,再调优惠券服务又是另一个数据库。跨服务、跨库、跨网络,本地事务锁不住资源,崩溃或超时后数据很容易不一致。
常见方案不是银弹
有人直接上 Seata,但引入 TC(事务协调器)又多了一个中间件要运维。TCC 写起来麻烦,每个接口都要写 confirm 和 cancel 逻辑,开发成本翻倍。而基于消息队列的最终一致性,虽然简单,但补偿逻辑得自己兜底,比如消息丢了怎么办?重试几次合适?
拿支付回调举例:用户付完钱,系统要更新订单状态、增加积分、通知物流准备打包。如果积分服务挂了,消息发到 MQ 后迟迟没消费,用户看到“支付成功”却没加积分,客服电话立马就响了。
务实的做法:按场景选型
核心交易链路比如下单支付,建议用 TCC 或 Saga 模式,保证强一致性。非关键路径,像日志记录、推荐数据同步,用消息队列+本地事务表更轻量。
比如用 RabbitMQ 处理订单日志:
INSERT INTO order_log (order_id, action) VALUES (1001, 'created');
-- 然后发送消息
INSERT INTO mq_message (exchange, routing_key, payload) VALUES ('logs', 'order.log', '{"id":1001}');
两个操作在一个本地事务里提交,要么都成功,要么都不进。后台有轮询任务把未发送的消息补发出去,确保最终投递。
服务治理不能少监控
无论用哪种事务方案,必须配上链路追踪。比如用 SkyWalking 或 Zipkin,一眼看出是卡在库存服务还是优惠券环节。配合告警规则,异常事务自动上报,别等用户投诉才去查。
微服务环境下,事务不是单纯技术问题,而是业务设计的一部分。拆服务的时候就得想好边界在哪里,哪些操作能容忍短暂不一致,哪些必须一步到位。工具只是辅助,关键是思路清晰。