楼层: 首页/ 软件技术/ 中间件全景/ 事务中间件:分布式事务与 Seata
07

事务中间件:分布式事务与 Seata

Distributed Transaction · 2PC / TCC / Seata / Saga

分布式事务就是跨多个数据库/服务的事务,要么全成功要么全回滚。你下单要扣库存(库存服务)、减余额(账户服务)、写订单(订单服务),三个服务三个数据库,怎么保证三步要么全成要么全撤?这就是事务中间件要解决的问题。

6.1 CAP 定理与 BASE 理论

论分布式系统的不可能三角

CAP 定理:一致性(Consistency)、可用性(Availability)、分区容错(Partition tolerance),三者只能取二。网络分区一定会发生(网线拔了),所以 P 必须保,实际只能在 C 和 A 之间选:CP(ZooKeeper,分区时拒绝服务保一致)或 AP(Eureka/Nacos,分区时还能服务但数据可能不一致)。

BASE 理论:基本可用(Basically Available)、软状态(Soft State)、最终一致(Eventually Consistent)。这是 CAP 的 AP 路线——牺牲强一致性,保证最终一致。绝大多数互联网系统走 BASE。

6.2 2PC:两阶段提交

2PC(Two-Phase Commit)是最经典的分布式事务协议。有一个协调者和多个参与者:

阶段一:准备
协调者问所有参与者:"能提交吗?" 参与者执行事务但不提交,回 Yes/No
全 Yes → 提交
协调者发 Commit,所有参与者正式提交
/
有人 No → 回滚
协调者发 Rollback,所有参与者回滚
2PC 的三个硬伤

① 同步阻塞:参与者等协调者指令时一直阻塞,资源不能干别的。② 单点故障:协调者挂了,参与者永远卡住。③ 数据不一致:Commit 阶段部分参与者收到、部分没收到(网络问题),数据就不一致了。

6.3 3PC:三阶段提交

3PC 在 2PC 基础上加了一个 CanCommit 阶段和超时机制,减少阻塞。但还是有数据不一致问题,而且更复杂。实际生产很少用 3PC,了解概念就行。

6.4 TCC:业务层两阶段

论TCC 的思路

TCC = Try-Confirm-Cancel,业务层面的两阶段。不是数据库层的锁,而是你自己写三个接口:

Try:预留资源(比如冻结库存、冻结余额,不真正扣减)。

Confirm:所有 Try 都成功了,正式执行(扣库存、减余额)。

Cancel:有 Try 失败了,回滚(解冻库存、解冻余额)。

三个经典问题:① 空回滚:Try 没到但 Cancel 到了,要处理;② 幂等:Confirm/Cancel 可能重复调用,要幂等;③ 悬挂:Cancel 先到 Try 后到,Try 不能执行。

6.5 Seata:阿里开源分布式事务框架

Seata 是现在国内最流行的分布式事务框架。它有三个角色:TC(Transaction Coordinator,事务协调者,独立部署)、TM(Transaction Manager,定义全局事务)、RM(Resource Manager,管分支事务)。支持四种模式:

Seata 四种模式对比
模式原理适合
AT自动补偿。一阶段直接提交本地事务,记录 undo log;二阶段根据全局结果决定提交或回滚(自动生成反向 SQL)大多数业务,无侵入,最常用
TCC手写 Try/Confirm/Cancel 三个接口高要求、性能好
Saga长事务,正向流程 + 逆向补偿跨多服务长流程
XA数据库层面 2PC,强一致传统数据库强一致场景

Seata AT 模式使用(Spring Boot 注解式)

# 订单服务:加 @GlobalTransactional 注解 @Service public class OrderService { @GlobalTransactional(name = "createOrder", rollbackFor = Exception.class) public void createOrder(Order order) { orderDAO.insert(order); # 本地事务,直接提交 stockClient.deduct(order.getSkuId(), order.getCount()); # 调库存服务 accountClient.debit(order.getUserId(), order.getAmount()); # 调账户服务 # 任何一步抛异常,Seata 自动回滚已执行的步骤 } }

6.6 Saga 与本地消息表

Saga 模式:把长事务拆成一系列本地事务,每个本地事务配一个补偿操作。正向执行:T1→T2→T3,任一步失败就逆向补偿:C3→C2→C1。适合流程长、参与方多的场景。

本地消息表:最朴素的最终一致性方案。业务操作和消息存在同一个本地事务里(要么都成要么都不成),然后定时任务把消息发到 MQ,下游消费。RocketMQ 的事务消息就是这个思路。

分布式事务方案选型对比
方案一致性性能复杂度适合场景
2PC/XA强一致差(同步阻塞)中传统数据库强一致
TCC最终一致好高(手写三接口)金融、资金类
Seata AT最终一致好低(注解式)大多数业务,推荐
Saga最终一致好中长流程跨服务
本地消息表最终一致好低异步解耦、日志类
6.7 事务中间件面试重点

Q1:CAP 定理实际怎么选?

查看答案

网络分区一定会发生,P 必须保。CP 选 ZooKeeper(强一致但分区时不可用),AP 选 Eureka/Nacos(高可用但可能短暂不一致)。互联网系统大多选 AP + 最终一致。

Q2:2PC 为什么有数据不一致?

查看答案

Commit 阶段协调者发 Commit 给所有参与者,如果网络问题部分收到部分没收到,收到的提交了、没收到的还锁着,数据就不一致了。

Q3:Seata AT 模式原理?

查看答案

一阶段直接提交本地事务(不锁),同时记录 undo log 和全局锁。二阶段:全局成功就删 undo log;全局失败就用 undo log 生成反向 SQL 回滚。无业务侵入,注解式。

记
第六章小结

① CAP 三选二,实际都保 P,互联网系统选 AP + 最终一致(BASE)。

② 2PC 有同步阻塞/单点/不一致三大硬伤;TCC 手写三接口但性能好;Seata AT 无侵入最常用。

③ 大多数业务用 Seata AT 或本地消息表就够了,别为了强一致上 XA。