在微服务架构中,一个业务流程往往需要多个微服务协同完成(如电商下单涉及订单、库存、支付、物流等服务),而每个微服务独立维护自己的数据库。分布式事务的核心目标是保证这些跨服务操作的一致性——即所有操作要么全部成功,要么全部失败,避免出现“部分成功、部分失败”的中间状态。
常见的分布式事务处理机制有多种,每种机制基于不同的设计思想(如强一致性、最终一致性),适用于不同的业务场景。以下是主流机制的详细介绍及实例说明:
一、两阶段提交(2PC,Two-Phase Commit)
原理
2PC是分布式事务中最经典的强一致性方案,通过“协调者”和“参与者”的协作,分两个阶段完成事务:
- 准备阶段(Prepare):协调者向所有参与者发送“准备指令”,参与者执行本地事务(如扣减库存、账户扣款),但不提交,并记录undo日志(用于回滚),然后反馈“是否就绪”(成功/失败)。
- 提交阶段(Commit):若所有参与者均反馈“就绪”,协调者发送“提交指令”,参与者执行提交;若有任一参与者失败,协调者发送“回滚指令”,参与者基于undo日志回滚。
优缺点
- 优点:能保证强一致性(ACID中的原子性)。
- 缺点:
- 同步阻塞:准备阶段所有参与者需等待其他节点响应,资源被锁定,性能低;
- 协调者单点风险:若协调者宕机,参与者可能长期阻塞;
- 数据不一致风险:提交阶段若部分参与者未收到“提交指令”,会导致部分提交、部分未提交。
适用场景
适用于对一致性要求极高、并发量低的场景(如金融核心交易)。
举例:银行跨账户转账
假设用户A(银行A服务)向用户B(银行B服务)转账100元:
- 准备阶段:协调者(银行总系统)向A服务发送“扣减100元”指令,A服务执行本地事务(余额-100),记录undo日志(余额+100),反馈“就绪”;同时向B服务发送“增加100元”指令,B服务执行(余额+100),记录undo日志(余额-100),反馈“就绪”。
- 提交阶段:协调者收到所有“就绪”,向A和B发送“提交指令”,两者提交事务,最终A余额-100、B余额+100;若B服务在准备阶段失败(如账户不存在),协调者发送“回滚指令”,A服务基于undo日志恢复余额。
二、TCC(Try-Confirm-Cancel)
原理
TCC是一种基于“业务侵入式”的补偿机制,要求每个微服务实现3个接口:
- Try:预留资源(检查并锁定资源,如扣减库存前先冻结部分库存);
- Confirm:确认执行(实际扣减资源,仅在所有服务Try成功后调用);
- Cancel:取消释放(若任一服务Try失败,释放已预留的资源)。
优缺点
- 优点:异步非阻塞,性能高;支持复杂业务逻辑;无资源锁定(Try阶段仅预留)。
- 缺点:开发成本高(需手动实现3个接口);依赖业务逻辑设计(补偿逻辑需严谨);需处理幂等性(避免重复调用Confirm/Cancel)。
适用场景
适用于核心业务(如支付、订单),且需要灵活控制资源预留的场景。
举例:电商订单支付
用户下单购买商品,涉及“订单服务”“库存服务”“账户服务”:
- Try阶段:
- 订单服务:创建“待支付”订单(预留订单ID);
- 库存服务:冻结商品库存(如商品库存100→冻结10,可用90);
- 账户服务:冻结用户账户余额(如余额500→冻结100,可用400)。
- Confirm阶段:若所有Try成功,执行确认:
- 订单服务:将订单状态改为“已支付”;
- 库存服务:实际扣减冻结的10个库存(可用90→80);
- 账户服务:实际扣减冻结的100元(可用400→300)。
- Cancel阶段:若账户服务Try失败(如余额不足),调用Cancel:
- 订单服务:删除“待支付”订单;
- 库存服务:释放冻结的10个库存(可用90→100);
- 账户服务:释放冻结的余额(可用400→500)。
三、Saga模式
原理
Saga将分布式事务拆分为一系列本地事务,每个本地事务对应一个“补偿事务”(用于失败时回滚)。事务按顺序执行,若某步失败,从失败点开始反向调用补偿事务,最终实现“最终一致性”。
Saga分为两种模式:
- 编排式:由一个“编排器”统一协调各服务的执行顺序和补偿逻辑;
- 协同式:各服务通过消息通知自主决定下一步操作(耦合度低,但逻辑复杂)。
优缺点
- 优点:支持长事务(如跨天的订单流程);无资源锁定,性能高;
- 缺点:仅保证最终一致性(中间可能有短暂不一致);补偿逻辑复杂(需处理网络超时、重复调用等)。
适用场景
适用于长事务、一致性要求不严格的场景(如电商下单、物流调度)。
举例:电商下单全流程
业务流程:创建订单→扣减库存→支付→创建物流单。
补偿流程:取消物流单→退款→恢复库存→取消订单。
正常执行:
- 订单服务:本地事务创建订单(状态“待支付”);
- 库存服务:本地事务扣减库存(如商品A库存100→99);
- 支付服务:本地事务扣减用户余额(如100元);
- 物流服务:本地事务创建物流单(状态“待发货”)。
失败回滚(假设支付服务失败,用户余额不足):
- 触发补偿:库存服务执行补偿(库存99→100);
- 订单服务执行补偿(订单状态改为“已取消”)。
四、本地消息表(Local Message Table)
原理
基于“本地事务+消息异步通知”实现最终一致性:
- 服务A执行本地事务时,同步将“需要通知其他服务的消息”写入本地消息表(与业务操作在同一事务中,保证消息必被记录);
- 异步线程读取消息表,将消息发送到消息队列(如RabbitMQ);
- 服务B消费消息,执行本地事务,成功后反馈“处理完成”;若失败,消息队列重试发送。
优缺点
- 优点:实现简单,不依赖复杂中间件;
- 缺点:消息表与业务表耦合(需在业务库中维护);需手动处理消息重试、幂等性(避免重复处理)。
适用场景
适用于中小规模系统,对一致性要求不高(最终一致即可)。
举例:用户注册送积分
- 注册服务执行本地事务:创建用户(user表),同时向本地消息表插入“用户A注册,需送100积分”的消息(状态“待发送”);
- 注册服务的异步线程读取“待发送”消息,发送到消息队列;
- 积分服务消费消息,执行本地事务:为用户A的积分账户增加100分,并记录“已处理”;
- 若积分服务处理失败(如网络中断),消息队列会重试发送,直到成功。
五、事务消息(Transactional Message)
原理
基于消息中间件的“事务消息”功能(如RocketMQ、Seata),通过“半消息”机制保证消息的可靠投递:
- 发送方发送“半消息”(消息中间件暂存,不投递);
- 发送方执行本地事务;
- 若本地事务成功,发送方通知中间件“确认发送”,消息投递到接收方;若失败,通知“取消发送”,消息删除。
优缺点
- 优点:解耦(无需本地消息表);依赖中间件保证可靠性;
- 缺点:依赖中间件支持(并非所有MQ都有事务消息功能)。
适用场景
适用于依赖消息中间件的分布式系统,如支付结果通知、订单状态同步。
举例:跨银行转账(A银行→B银行)
- A银行发送“半消息”到RocketMQ:“A用户向B用户转账500元”;
- A银行执行本地事务:扣减A用户500元(余额-500);
- 若扣减成功,A银行通知MQ“确认发送”,消息投递到B银行;
- B银行消费消息,执行本地事务:增加B用户500元(余额+500);
- 若A银行扣减失败(如余额不足),通知MQ“取消发送”,消息被删除,B银行无操作。
六、最大努力通知(Best Effort Delivery)
原理
“发起方”尽最大努力通知“接收方”执行操作,若多次通知失败,则记录日志,由人工介入处理(放弃自动一致性)。
优缺点
- 优点:实现最简单,成本低;
- 缺点:一致性最弱(可能需要人工干预)。
适用场景
适用于对一致性要求极低的场景(如支付结果通知、短信提醒)。
举例:支付结果通知商户
- 支付平台完成用户支付后,向商户系统发送“支付成功”通知;
- 若商户系统未响应,支付平台重试(如10分钟内重试5次);
- 若仍失败,支付平台记录日志(“商户A未收到支付结果”),由运营人员手动通知商户。
总结
分布式事务机制的选择需结合业务场景(一致性要求、并发量、事务长度):
- 强一致性(如金融核心交易):优先2PC、TCC;
- 最终一致性+长事务(如电商):优先Saga、事务消息;
- 简单场景+低成本:本地消息表、最大努力通知。
实际应用中,往往会组合多种机制(如TCC+事务消息)以平衡一致性和性能。