微服务数据一致性解决方案
2026-09-23 14:02:19 0 举报服务一拆,数据就乱?扣了钱库存没减、重复扣款、订单对不上。这份脑图不扯虚的,把2PC、TCC、Saga、事务消息、Seata掰开揉碎讲透,教你什么场景用什么方案,顺手填平幂等、补偿的坑,从此告别数据不一致!
分布式事务
微服务
Seata
数据最终一致性
幂等设计
模板推荐
作者其他创作
大纲/内容
🎯 一、问题本质:为什么微服务下数据一致性这么难
📖 1.1 从一个真实业务场景说起(贯穿全文的例子)
场景:用户下单购买商品
订单服务:创建订单(写入订单数据库)
库存服务:扣减库存(写入库存数据库)
账户服务:扣减余额(写入账户数据库)
这三个操作必须"要么全部成功,要么全部失败"
单体架构下:三个操作在同一个数据库,一个本地事务(BEGIN...COMMIT)就能保证原子性
微服务架构下:三个服务各有独立数据库(Database per Service),无法用一个本地事务
核心矛盾:业务上要求原子性,但技术上没有跨库事务能力
📖 1.2 微服务拆分带来的事务边界破碎
设计原则:每个微服务拥有自己的数据库(数据自治)
好处:服务解耦、独立演进、技术栈灵活
代价:原本一个数据库内的操作,被拆分到多个数据库
事务边界的物理隔离
本地事务只能保证单个数据库内的ACID
跨服务调用(HTTP/RPC)无法纳入同一个事务
服务A提交后,服务B可能失败,此时服务A已提交无法回滚
📖 1.3 分布式环境的三大不可靠(一致性问题的根源)
网络不可靠
服务间调用可能超时、丢包、乱序
你无法区分"对方没收到"还是"对方收到了但响应丢了"
导致:调用方不知道该重试还是放弃
节点不可靠
任何服务实例都可能随时宕机
事务执行到一半,节点挂了,状态悬而未决
导致:部分操作已执行,部分未执行
时序不可靠
分布式系统没有全局时钟,各节点时钟有偏差
消息到达顺序可能与发送顺序不一致
导致:无法简单通过时间戳判断事件先后
📖 1.4 一致性问题的本质定义
数据一致性:多个数据副本或多个关联数据之间保持业务规则约束的状态
微服务数据一致性:跨服务、跨数据库的关联数据,在业务规则上保持正确状态
例:订单状态为"已支付",但库存未扣减 → 不一致
例:账户余额扣了,但订单状态还是"待支付" → 不一致
核心挑战:在没有全局事务的情况下,如何保证跨服务数据的业务正确性
📐 二、理论基础:你需要哪种一致性(认知校准)
📖 2.1 CAP定理中与一致性相关的部分
C(Consistency)一致性:所有节点看到的数据一致
A(Availability)可用性:请求能得到响应
P(Partition Tolerance)分区容错:网络分区时系统可用
关键认知:分布式系统必须容忍网络分区(P是客观存在的),所以只能在C和A之间权衡
对数据一致性的启示:
追求强一致性(CP)→ 牺牲可用性,网络分区时拒绝服务
追求高可用(AP)→ 牺牲强一致性,接受短暂数据不一致
绝大多数互联网业务选择AP + 最终一致性
📖 2.2 一致性级别(从强到弱)
强一致性
定义:数据写入后,任何后续读取都能读到最新值
代价:高延迟、低可用、吞吐量受限
适用:资金转账、证券交易等对一致性要求极高的场景
最终一致性
定义:不保证实时一致,但在一定时间后,数据最终会达到一致状态
代价:存在短暂的不一致窗口(通常秒级到分钟级)
适用:绝大多数互联网业务(电商订单、社交动态、内容发布)
关键认知
不要盲目追求强一致性,大多数业务可以接受最终一致性
选型第一步:明确业务能容忍多久的不一致、多大的不一致范围
📖 2.3 本地事务的ACID与分布式的冲突
本地事务ACID
原子性(Atomicity):事务内操作要么全成功要么全失败
一致性(Consistency):事务前后数据满足约束
隔离性(Isolation):并发事务互不干扰
持久性(Durability):提交后数据持久化
分布式的困境
原子性:跨服务无法用一个事务包裹
一致性:跨库约束无法由数据库保证
隔离性:跨服务的并发控制极其复杂
持久性:这个相对好保证(各服务本地保证)
核心结论:分布式事务要解决的核心是"跨服务的原子性和一致性"
🔒 三、刚性事务方案:追求强一致性
📖 3.1 2PC 两阶段提交(理解分布式事务的基石)
核心思想
引入一个"协调者"(Coordinator),统一指挥所有"参与者"(Participant)
把事务分成两个阶段:先"准备",再"提交/回滚"
所有参与者在阶段一做好准备,阶段二统一行动
角色定义
协调者(Coordinator):全局事务的指挥者,负责发起和协调
通常由事务管理器(TM)或某个服务承担
参与者(Participant):实际执行本地事务的服务/数据库
每个参与者管理自己的本地事务和资源
阶段一:Prepare(准备/投票阶段)详细流程
步骤1:协调者向所有参与者发送Prepare请求("大家准备好了吗?")
步骤2:每个参与者收到请求后,执行本地事务操作
执行业务SQL(如INSERT订单、UPDATE库存)
写入redo日志(重做日志)和undo日志(回滚日志)
锁定相关资源(行锁或表锁),防止其他事务修改
关键:执行了操作但【不提交】,事务处于"悬而未决"状态
步骤3:参与者向协调者返回响应
执行成功 → 返回ACK(Yes,我准备好了)
执行失败 → 返回NAK(No,我失败了)
为什么要锁定资源:在等待阶段二期间,不能让其他事务修改这些数据,否则无法保证一致性
阶段二:Commit(提交阶段)详细流程
情况A:所有参与者都返回Yes
步骤1:协调者决定提交,向所有参与者发送Commit请求
步骤2:参与者收到Commit后,提交本地事务,释放锁
步骤3:参与者向协调者返回提交完成确认
步骤4:协调者收到所有确认后,全局事务完成
情况B:有参与者返回No 或 超时未响应
步骤1:协调者决定回滚,向所有参与者发送Rollback请求
步骤2:参与者收到Rollback后,利用undo日志回滚本地事务,释放锁
步骤3:参与者向协调者返回回滚完成确认
步骤4:协调者收到所有确认后,全局事务回滚完成
2PC的四大致命缺陷(必须深刻理解)
缺陷1:同步阻塞
阶段一完成后,参与者锁定资源,等待协调者的阶段二指令
等待期间,相关资源被锁,其他事务无法访问
结果:吞吐量极低,不适合高并发场景
缺陷2:协调者单点故障
协调者宕机后,参与者一直等待阶段二指令
参与者不知道该提交还是回滚,只能一直阻塞
资源被锁无法释放,可能导致系统瘫痪
缺陷3:数据不一致
阶段二协调者发送Commit时,如果部分参与者收到了、部分没收到(网络分区)
收到的参与者提交,没收到的参与者可能超时回滚
结果:部分数据提交、部分回滚,数据不一致
缺陷4:网络分区下的不确定性
参与者与协调者网络断开,参与者无法得知全局决策
参与者只能根据自己的超时策略决定(默认阻塞或默认提交),但无法保证全局一致
2PC的适用场景与结论
适用:传统企业应用、对一致性要求极高且并发量小的场景
不适用:高并发互联网微服务
结论:2PC是理解分布式事务的基础,但生产中很少直接使用
📖 3.2 3PC 三阶段提交(理论改进,了解即可)
核心改进
在2PC基础上增加了一个CanCommit阶段(预询问)
三阶段:CanCommit → PreCommit → DoCommit
引入了超时机制:参与者超时后默认提交(而非阻塞)
为什么实际不用
仍然无法解决网络分区下的数据不一致
实现复杂度更高
超时默认提交可能带来新的不一致问题
结论:3PC是理论上的改进,工程实践中基本不采用
📖 3.3 TCC 补偿型事务(生产级强一致方案)
核心思想
把资源锁定从"数据库层"提升到"业务层"
不依赖数据库的锁机制,而是通过业务逻辑实现"预留"和"补偿"
业务需要实现三个接口:Try(预留)、Confirm(确认)、Cancel(取消)
用下单例子理解TCC
Try阶段:预留资源
库存服务:不直接扣减库存,而是"冻结"库存
例:库存100,用户买2件 → 可用库存变为98,冻结库存变为2
数据还在库存服务,只是状态变了
账户服务:不直接扣款,而是"冻结"金额
例:余额1000,消费200 → 可用余额800,冻结金额200
订单服务:创建"待确认"状态的订单
Confirm阶段:确认执行
所有服务Try成功后,协调者调用各服务的Confirm
库存服务:将冻结的2件库存真正扣减(冻结库存清零,实际库存减少)
账户服务:将冻结的200元真正扣减(冻结金额清零,实际余额减少)
订单服务:订单状态从"待确认"变为"已确认"
Cancel阶段:取消补偿
任一服务Try失败,协调者调用已Try成功服务的Cancel
库存服务:将冻结的2件库存释放回可用(可用库存恢复,冻结清零)
账户服务:将冻结的200元解冻回可用余额
订单服务:取消"待确认"订单
TCC的三大坑(生产中必须处理,否则出大问题)
坑1:幂等性问题
问题:Confirm或Cancel可能因网络重试被多次调用
后果:库存被重复扣减、金额被重复扣款
解决:Confirm和Cancel接口必须幂等
通过唯一事务ID判断是否已处理过
已处理过则直接返回成功,不重复执行
坑2:空回滚问题
问题:Try请求因网络原因未到达参与者,但Cancel请求到达了
后果:Cancel要回滚一个从未执行的Try,可能报错或产生脏数据
解决:
参与者维护一张事务控制表,记录分支事务状态
Cancel时先查表:如果没有对应的Try记录,说明是空回滚
空回滚时:插入一条"已回滚"记录,直接返回成功
坑3:悬挂问题
问题:Cancel先于Try到达(网络乱序),Cancel执行后,迟到的Try又执行了
后果:资源被预留但永远不会被Confirm或Cancel,造成资源泄漏
解决:
Try执行前,先查事务控制表
如果已存在"已回滚"记录,说明Cancel已执行,Try直接拒绝执行
TCC的优缺点
优点
不依赖数据库锁,性能优于2PC
业务层控制,灵活性高,可精细控制资源预留
最终一致性保证
缺点
业务侵入性极强:每个操作要写Try/Confirm/Cancel三个接口
开发成本高:对业务建模能力要求高(如何设计"预留")
不适合无法"预留"的业务(如发送短信无法"预留")
TCC适用场景
资金类:冻结→扣款/解冻(支付、转账)
库存类:预占→扣减/释放(电商库存)
核心交易链路,对一致性要求高且并发量大
📖 3.4 XA 标准接口(数据库层面的2PC)
核心概念
XA是X/Open组织定义的分布式事务标准接口
本质:把2PC的流程标准化,由数据库实现
角色:事务管理器(TM)+ 资源管理器(RM,通常是数据库)
核心接口
xa_start:开启分支事务
xa_end:结束分支事务操作
xa_prepare:准备提交(2PC阶段一)
xa_commit:提交分支事务
xa_rollback:回滚分支事务
在微服务中的应用
Seata的XA模式就是基于数据库XA协议
优点:对业务代码无侵入,数据库层面保证强一致
缺点:性能差(长时间持有数据库连接和锁),依赖数据库支持
适用场景
传统企业应用、并发量小、不愿改业务代码
不适合高并发互联网场景
🌊 四、柔性事务方案:追求最终一致性(互联网主流)
📖 4.1 Saga 长事务模式(跨多服务的长流程首选)
核心思想
将一个长事务拆分为多个本地短事务
每个本地事务都有一个对应的"补偿操作"
按顺序执行本地事务,失败时按逆序执行补偿
用旅行预订例子理解Saga
正向流程:订机票 → 订酒店 → 订租车
每一步是一个本地事务,各自提交
如果"订租车"失败
逆序补偿:取消酒店 → 取消机票
最终状态:所有预订都取消,回到初始状态
两种实现模式(重点区分)
编排式(Choreography)
原理:无中心协调者,服务间通过事件驱动串联
流程:服务A完成→发事件→服务B监听并执行→发事件→服务C监听并执行
补偿:服务C失败→发补偿事件→服务B监听并补偿→服务A监听并补偿
优点:服务间松耦合,简单场景适用
缺点:流程分散在各服务,复杂场景难维护,可能产生循环依赖
适用:步骤少(3步以内)的简单流程
协调式(Orchestration)
原理:有一个中心协调者(Saga Orchestrator)统一编排
流程:协调者按顺序调用服务A→B→C,失败时按逆序调用补偿
优点:流程集中管理,易于监控、调试、维护
缺点:协调者可能成为单点(需高可用部署)
适用:步骤多、流程复杂的业务场景(生产主流)
Saga的关键设计要点
每个本地事务必须幂等(可能重试)
每个补偿操作必须幂等(可能重试)
补偿操作也可能失败 → 需要重试机制 + 最终人工兜底
隔离性问题:Saga执行中,中间状态对外可见
例:机票已订但酒店还没订时,用户能看到机票已订
缓解:业务状态标识("预订中"),语义锁
Saga的优缺点
优点:无全局锁,性能好;适合长流程;不要求业务支持"预留"
缺点:无隔离性;补偿逻辑设计复杂;最终一致性
Saga适用场景
跨多个服务的长流程(旅行预订、电商下单、物流履约)
无法用TCC"预留"的业务
📖 4.2 本地消息表(最经典的最终一致性实现)
核心原理
将"业务操作"和"消息写入"放在同一个本地事务中
利用本地事务的ACID,保证业务和消息的原子性
业务成功后,通过后台任务将消息发送到MQ,下游消费
用下单例子理解本地消息表
订单服务:创建订单 + 写入消息表(同一个本地事务)
消息内容:{订单ID, 类型:"订单已创建", 需通知:库存服务、积分服务}
后台任务:轮询消息表,将"待发送"的消息发送到MQ
库存服务:消费消息,扣减库存
积分服务:消费消息,增加积分
完整流程(7步)
步骤1:订单服务在本地事务中执行"创建订单"+"插入消息表"
消息状态:待发送
关键:这两个操作在同一个事务,要么都成功要么都失败
步骤2:后台定时任务扫描消息表中"待发送"状态的记录
步骤3:将消息发送到MQ
步骤4:发送成功 → 更新消息状态为"已发送"
步骤5:下游服务(库存、积分)从MQ消费消息
步骤6:下游处理成功 → 回调上游确认(或上游查询下游状态)
步骤7:确认成功 → 更新消息状态为"已完成"
消息表结构设计
消息ID:全局唯一(UUID或雪花算法)
业务ID:关联的业务对象(如订单ID)
消息内容:JSON,包含下游需要的数据
状态:待发送/已发送/已完成/失败
重试次数:记录已重试次数
创建时间、更新时间
关键设计要点
下游消费必须幂等:基于消息ID去重(消息可能重复投递)
发送失败自动重试:设置最大重试次数(如5次)
死信处理:超过最大重试次数,进入死信表,人工干预
轮询性能:消息量大时需分片扫描、建索引
优缺点
优点:实现简单,不依赖特殊MQ功能,任何MQ都可用
缺点:消息表与业务表耦合,有轮询延迟,对数据库有额外压力
适用场景
中小型系统、消息量不大
无法使用事务消息的场景
跨异构系统的数据同步
📖 4.3 RocketMQ 事务消息(生产级可靠消息方案)
核心原理
利用RocketMQ的"半消息"机制,将本地事务和消息发送绑定
半消息(Half Message):消息已发送到Broker,但对消费者不可见
本地事务的执行结果,决定半消息是"投递"还是"丢弃"
核心概念:半消息
什么是半消息:生产者发送到Broker,但消费者看不到的消息
存储位置:Broker内部的特殊Topic(RMQ_SYS_TRANS_HALF_TOPIC)
作用:作为"临时存储",等待本地事务结果决定是否转为正常消息
完整流程(两阶段 + 回查)
阶段一:发送半消息
步骤1:生产者发送半消息到Broker
步骤2:Broker存储半消息(消费者不可见),返回发送成功
执行本地事务
步骤3:生产者执行本地业务操作(如创建订单)
步骤4:根据本地事务结果,决定下一步
阶段二:Commit/Rollback
情况A:本地事务成功
步骤5a:生产者向Broker发送Commit
步骤6a:Broker将半消息转为正常消息,消费者可见
情况B:本地事务失败
步骤5b:生产者向Broker发送Rollback
步骤6b:Broker丢弃半消息
回查机制(关键保障)
问题:如果生产者发送半消息后宕机,没有发送Commit/Rollback怎么办?
解决:Broker定时回查生产者的本地事务状态
Broker每隔一段时间(默认6秒)向生产者发起回查
生产者查询本地事务状态(如查数据库中的订单是否存在)
返回Commit/Rollback/Unknown
最大回查次数:默认15次,超过后默认Rollback
与本地消息表的对比
本地消息表:消息存在业务数据库,需轮询发送
事务消息:消息存在MQ,由MQ保证投递,无需轮询
事务消息性能更好,但强依赖RocketMQ
优缺点
优点:无需本地消息表,不侵入业务数据库,性能好,生产级成熟
缺点:强依赖RocketMQ,回查机制需业务支持状态查询
适用场景
互联网核心业务(电商、支付、物流)
消息量大、对性能有要求的场景
已使用RocketMQ的团队
📖 4.4 最大努力通知(最弱的一致性保证)
核心思想
上游尽最大努力通知下游,但不保证一定成功
下游提供回调接口,上游多次重试通知
超过重试次数后放弃,由对账或人工处理
与可靠消息的区别
可靠消息(本地消息表/事务消息):消息持久化,保证一定投递
最大努力通知:消息不持久化,仅重试几次,可能丢失
适用场景
非核心业务通知(发短信、发邮件、推送通知)
跨企业/跨平台对接(如支付结果通知商户)
允许一定丢失率、可通过其他途径补偿的场景
🔐 五、一致性方案的三大支撑机制(没有这些,方案会失效)
📖 5.1 幂等性设计(所有一致性方案的前提)
为什么幂等性如此重要
网络重试:调用超时后重试,可能导致重复请求
消息重复消费:MQ的At Least Once语义,消息可能重复投递
用户重复提交:前端重复点击
补偿重复执行:Cancel/补偿可能多次调用
没有幂等性:库存重复扣减、金额重复扣款、订单重复创建
幂等性的定义
同一个操作执行一次和执行多次,产生的结果相同
例:SET a=5 执行多次结果都是a=5(幂等)
例:a=a+1 执行多次结果不同(非幂等)
幂等性实现方案(按场景)
方案1:唯一索引/唯一约束
原理:数据库对业务唯一键建唯一索引
实现:订单表对"订单号"建唯一索引,重复插入报错
适用:插入操作
优点:简单可靠,数据库层面保证
方案2:状态机控制
原理:业务状态只能单向流转,不可逆
实现:订单状态 待支付→已支付→已发货→已完成
重复操作时检查当前状态,不合法则拒绝
适用:有明确状态流转的业务
方案3:乐观锁(版本号)
原理:更新时带版本号,版本不匹配则更新失败
实现:UPDATE account SET balance=balance-100, version=version+1 WHERE id=1 AND version=5
适用:更新操作
方案4:Token机制(防重放)
原理:请求前获取一次性Token,请求时携带,服务端验证后删除
实现:重复请求因Token已删除而被拒绝
适用:表单提交、支付确认
方案5:去重表/幂等表
原理:独立表记录已处理的请求(业务ID+操作类型)
实现:处理前查去重表,已存在则直接返回成功
适用:消息消费幂等
方案6:全局唯一ID + Redis去重
原理:每个请求分配全局唯一ID,Redis SETNX记录
实现:重复请求因Redis已存在该ID而被拒绝
适用:高并发场景
幂等性设计原则
所有对外暴露的写接口必须幂等
幂等键要用业务语义的唯一标识(订单号优于随机UUID)
幂等判断和业务操作要在同一事务中
📖 5.2 补偿机制设计(Saga/TCC的核心)
补偿操作的三要素
可逆性:补偿能撤销原操作的影响(或达到等效业务状态)
幂等性:补偿可安全重复执行
独立性:补偿不依赖原操作的执行细节
补偿的两种模式
逆向补偿:执行原操作的逆操作(扣款→退款)
正向补偿:执行新操作达到期望状态(订单取消→释放库存)
补偿失败怎么办
自动重试:补偿失败时自动重试(指数退避)
人工干预:重试超过阈值,告警并转人工处理
记录补偿日志:完整记录补偿执行链路,便于审计
补偿设计注意事项
补偿可能依赖外部系统(外部不可用时补偿也失败)
补偿的时效性:某些业务有补偿时间窗口(优惠券过期后无法退回)
补偿的副作用:补偿可能触发其他副作用(退款触发通知)
📖 5.3 重试与退避策略
什么时候该重试
瞬时故障(网络抖动、短暂不可用)→ 可以重试
确定性错误(参数错误、业务校验失败)→ 不应重试
非幂等操作 → 不能盲目重试(先确保幂等)
退避策略
固定间隔:每次重试间隔相同(简单但不智能)
指数退避:间隔按2的幂次增长(1s→2s→4s→8s)
指数退避+抖动:加随机抖动,避免重试风暴
最大重试次数:设置上限,超过后进入死信/人工处理
重试风暴防护
限流:限制单位时间内的重试次数
熔断:下游持续不可用时,熔断重试(Circuit Breaker)
降级:重试失败后走降级逻辑
🏗️ 六、Seata:生产级分布式事务框架(原理详解)
📖 6.1 Seata整体架构
三大角色
TC(Transaction Coordinator)事务协调器
独立部署的服务,维护全局事务和分支事务的状态
驱动全局事务的提交或回滚
TM(Transaction Manager)事务管理器
定义全局事务的范围,发起、提交、回滚全局事务
通常嵌入在发起全局事务的服务中
RM(Resource Manager)资源管理器
管理分支事务处理的资源(通常是数据库)
向TC汇报分支事务状态,执行分支事务的提交/回滚
工作流程
TM向TC申请开启全局事务,获得全局事务ID(XID)
XID通过RPC调用传播到各微服务
各微服务的RM将本地事务注册为分支事务,关联到XID
各RM执行本地事务,向TC汇报结果
TM根据各分支结果,决议提交或回滚全局事务
📖 6.2 Seata AT模式(最常用,重点理解)
核心思想
基于本地事务 + 全局锁 + 自动回滚日志
一阶段直接提交本地事务(高性能),二阶段异步处理回滚日志
对业务代码几乎无侵入(只需加注解)
一阶段:执行业务并生成回滚日志
步骤1:解析业务SQL,获取"前镜像"(修改前的数据)
步骤2:执行业务SQL(如UPDATE)
步骤3:获取"后镜像"(修改后的数据)
步骤4:将前镜像和后镜像生成undo_log(回滚日志)
步骤5:将业务数据和undo_log在同一个本地事务中提交
关键:业务数据和undo_log原子提交,保证一致
步骤6:向TC注册分支事务,申请全局锁
为什么一阶段能直接提交:因为生成了undo_log,即使提交了,也能通过undo_log回滚
二阶段:根据全局事务结果处理
情况A:全局提交
步骤1:异步删除undo_log(因为已提交,不需要回滚了)
特点:异步执行,不阻塞业务,性能高
情况B:全局回滚
步骤1:根据undo_log中的前镜像,生成反向SQL
步骤2:执行反向SQL,将数据恢复到修改前的状态
步骤3:删除undo_log
例:原操作是UPDATE stock SET count=8 WHERE id=1(前镜像count=10)
反向操作:UPDATE stock SET count=10 WHERE id=1
全局锁机制(保证写隔离)
问题:多个全局事务同时修改同一数据,会冲突
解决:TC维护全局锁,分支事务修改数据前需申请全局锁
写隔离:一阶段提交前必须持有全局锁,避免脏写
读隔离:AT模式默认读未提交(全局事务未提交前,其他事务可能读到中间状态)
AT模式的优缺点
优点:业务零侵入(只需加@GlobalTransactional注解),性能优于XA
缺点:依赖全局锁,热点数据并发高时性能下降;依赖关系型数据库
AT模式适用场景
大多数CRUD场景,中低并发
不愿大幅修改业务代码的场景
📖 6.3 Seata TCC模式
与前面TCC方案一致
Seata提供了TCC的框架支持,简化开发
业务需实现Try/Confirm/Cancel三个接口
适用:高性能要求的核心链路
📖 6.4 Seata Saga模式
与前面Saga方案一致
Seata提供状态机引擎,通过JSON配置定义Saga流程
适用:长流程、跨多个服务
📖 6.5 Seata XA模式
与前面XA方案一致
基于数据库XA协议,强一致性
适用:强一致性要求、并发量小
📖 6.6 Seata四种模式对比与选型
一致性:XA > AT > TCC/Saga
性能:TCC > Saga > AT > XA
业务侵入:TCC(最高)> Saga > AT > XA(最低)
选型建议
中低并发、不愿改代码 → AT模式
高性能核心链路 → TCC模式
长流程跨多服务 → Saga模式
强一致性、并发小 → XA模式
🎯 七、方案选型:架构师如何决策
📖 7.1 选型决策树(一步步判断)
第一步:能否避免分布式事务?
能否通过服务边界重新设计,将强一致操作放在同一服务?→ 优先避免
能否将强一致需求降级为最终一致?→ 优先降级
结论:最好的分布式事务是"不需要分布式事务"
第二步:一致性级别要求?
强一致性(资金、证券)→ 刚性事务(TCC/XA/2PC)
最终一致性(电商、社交)→ 柔性事务(Saga/消息/本地消息表)
第三步:性能与并发要求?
高并发(万级TPS以上)→ TCC / 事务消息
中低并发(千级TPS以下)→ Seata AT / XA
长流程跨多服务 → Saga
第四步:业务侵入性容忍度?
零侵入 → Seata AT / XA
轻度侵入 → 事务消息 / 本地消息表
重度侵入可接受 → TCC / Saga
第五步:技术栈与基础设施?
已有RocketMQ → 事务消息
Spring Cloud生态 → Seata
需要强一致且并发小 → XA
📖 7.2 各方案对比矩阵
一致性强度:XA > 2PC > TCC > Seata AT > Saga > 事务消息 > 本地消息表 > 最大努力通知
性能:TCC > 事务消息 > Saga > 本地消息表 > Seata AT > XA > 2PC
业务侵入性:TCC(最高)> Saga > 本地消息表 > 事务消息 > Seata AT > XA(最低)
实现复杂度:TCC > Saga > Seata AT > 事务消息 > 本地消息表 > XA
📖 7.3 典型场景方案推荐
场景1:电商下单(订单+库存+优惠券)
推荐:Saga(协调式)+ 事务消息
库存扣减可用TCC(预占模式)
场景2:支付转账(扣款+入账)
推荐:TCC(冻结→扣款/解冻)
或:本地事务+事务消息(单库扣款,消息通知入账)
场景3:库存扣减(高并发)
推荐:Redis预扣减 + 异步落库 + 对账
或:TCC预占模式
场景4:跨服务通知(发短信、发邮件)
推荐:最大努力通知 / 事务消息
🛡️ 八、兜底与保障:对账与异常处理
📖 8.1 对账机制(一致性的最后防线)
对账的本质
不依赖实时一致性,通过事后比对发现并修正不一致
是"最终一致性"的最终保障
即使分布式事务方案完美,也建议有对账兜底
对账实现
数据采集:各服务的交易流水通过日志/接口采集
数据比对:上游记录 vs 下游记录,比对金额、状态、业务ID
差异处理
自动修正:明确差异自动修复(如状态不一致则更新)
人工介入:复杂差异(如金额不一致)转人工
记录差异日志:所有差异留痕,便于审计
对账频率
资金类:实时 + T+1双重对账
订单类:准实时(5分钟)+ T+1
通知类:T+1即可
📖 8.2 异常处理与人工干预
异常分类
瞬时异常(网络抖动)→ 自动重试
确定性异常(参数错误)→ 记录日志,不重试
不确定异常(超时未知结果)→ 查询确认后决定重试/补偿
异常处理流程
自动重试(3次以内)→ 自动补偿 → 记录异常 → 人工介入
人工干预平台
异常事务列表:展示所有需人工处理的事务
事务详情:完整执行链路、日志、状态
操作:手动重试/手动补偿/标记已处理
操作日志:所有人工操作留痕
收藏
立即使用
收藏
立即使用
收藏
立即使用
收藏
立即使用
Collect
Get Started
Collect
Get Started
Collect
Get Started
Collect
Get Started
评论
0 条评论
下一页