这两个概念是微服务架构中处理分布式事务的核心。简单来说:
- 最终一致性 :是一种设计思想,为了系统可用性,允许数据短暂不一致,但保证最终会一致。
- Seata :是一个具体工具,它用"最终一致性"等思想,帮你实现这种效果。
🤔 什么是"最终一致性"?
要理解它,得先看一个更根本的问题:为什么在微服务里,保证数据一致这么难?
问题根源:从本地事务到分布式事务
在传统单体应用中,多个数据操作(如扣库存、生成订单)可以在同一个数据库事务(ACID)里完成,要么全成功,要么全失败。
但在微服务里,一个业务操作(比如下单)可能涉及订单服务 、支付服务 、库存服务 等多个独立服务,每个服务都有自己的数据库。要让这些跨服务的操作保持原子性,就变成了一个分布式事务问题。如果缺乏协调,就可能出现"钱扣了,但订单没生成"这种数据不一致的严重故障。
CAP定理与BASE理论
为什么分布式事务这么难?因为分布式系统受限于CAP定理:
- C(一致性):所有节点数据随时一致。
- A(可用性):服务始终可用。
- P(分区容错性):网络故障时系统仍能工作。
在网络故障(P)必然发生时,你必须在一致性(C) 和可用性(A) 之间做权衡。
为了保证高可用(A) ,业界提出了BASE理论:
- 基本可用(Basically Available):允许系统部分功能受损。
- 软状态(Soft State):允许数据中间状态,可以暂时不一致。
- 最终一致性(Eventually Consistent):系统保证经过一段时间后,数据最终会达到一致状态。
这就是最终一致性 的核心:用"暂时的不一致"换取"系统的高可用"。
🛠️ Seata 是什么?怎么解决?
Seata 是阿里巴巴开源的分布式事务解决方案。它就像一个全局的"事务协调员",把多个微服务的本地事务串联成一个全局事务。
Seata的核心组件
Seata通过三个核心角色协同工作:
- 事务协调者 (TC):独立部署的中央服务器,负责协调和管理所有全局事务的状态。
- 事务管理器 ™:嵌入在发起全局事务的服务(如订单服务)中,负责开启、提交或回滚全局事务。
- 资源管理器 (RM):嵌入在参与事务的每个服务中,负责执行本地事务,并向TC注册和报告状态。
Seata的四种事务模式
Seata提供了四种模式,其中后三种都基于最终一致性思想。
| 模式 | 一致性 | 业务侵入 | 核心思想 | 适用场景 |
|---|---|---|---|---|
| XA模式 | 强一致性 | 无侵入 | 基于数据库的两阶段提交(2PC),在准备阶段锁定资源,所有参与者就绪后统一提交或回滚。 | 对数据一致性要求极高、能接受性能损耗的场景。 |
| AT模式 | 最终一致性 | 无侵入 | Seata的默认模式 。通过解析SQL,自动生成并记录回滚日志(Undo Log)。业务提交后,若需回滚则用日志恢复数据。 | 大多数标准微服务场景,希望无侵入地解决分布式事务问题。 |
| TCC模式 | 最终一致性 | 有侵入 | 业务代码需实现 Try(预留资源)、Confirm(确认执行)、Cancel(补偿回滚) 三个接口。 | 核心业务 ,对性能要求高,需要精细控制资源锁定粒度。 |
| SAGA模式 | 最终一致性 | 有侵入 | 将长事务拆分为一系列本地事务 ,每个事务有对应的补偿操作。失败时,逆序执行补偿。 | 长事务、业务流程复杂、参与者可能不是数据库的遗留系统。 |
💎 总结
- 最终一致性 :是一种设计哲学,指导我们在分布式系统中,为了高可用性,可以接受数据的短暂不一致。
- Seata :是一个实用工具 ,它提供了AT、TCC等多种模式,帮我们把"最终一致性"这个思想落地,以不同的方式管理跨服务的数据一致性。
好的,我们接着上次的话题,用一个具体的电商下单场景,把"最终一致性"和Seata的四种模式掰开揉碎了讲清楚。
想象一下,我们正在搭建一个微服务架构的电商系统。用户下单这个看似简单的动作,背后需要订单服务 、库存服务****和账户服务**协同完成。
🚀 从下单场景看分布式事务
这个场景的核心流程是:
- 订单服务创建一条"待支付"的订单。
- 库存服务扣减相应商品的库存。
- 账户服务扣减用户的账户余额。
问题在于,这三个操作分别由三个独立的服务(和三个独立的数据库)完成。如何保证它们要么全部成功 ,要么全部失败,就是分布式事务要解决的核心问题。
💡 方案一:"最终一致性"的异步消息方案
"最终一致性"是一种务实的策略,它允许数据在短时间内不一致,但承诺最终会达成一致。
核心思想 :订单服务创建订单后,不直接调用库存和账户服务,而是发个消息到消息队列(MQ),让下游服务自己去处理。
案例:本地消息表 + RocketMQ
-
业务执行与记录消息(在一个本地事务里) :订单服务在同一个数据库事务中,同时做两件事:
- 插入一条订单数据。
- 向一张
local_message表里,插入一条"待发送"的消息,内容为"订单已创建,请扣减库存和余额"。
-
可靠消息投递 :一个后台定时任务,会扫描
local_message表,将"待发送"的消息投递到RocketMQ。如果投递失败,会重试,直到成功。 -
下游服务消费:库存服务和账户服务各自订阅并消费这条消息,执行本地的扣减操作。
最终一致性体现 :在订单创建和消息发送之间,或消息处理过程中,数据是不一致的。但通过消息重试 和幂等性设计 (保证重复消息不会导致重复扣减),系统能保证所有操作最终都会成功,数据最终一致。这个方案性能好、解耦,但开发量较大,且实时性稍弱。
🛠️ 方案二:Seata的四种模式及案例
Seata把分布式事务的管理集中化,通过一个全局的"事务协调者"来掌控全局。
1. AT 模式:无侵入的自动化方案 (Automatic Transaction)
这是Seata的默认模式 ,对代码无侵入,非常适合快速解决大多数分布式事务问题。
-
核心思想 :Seata通过代理数据源,自动记录每条SQL的回滚日志(undo_log)。如果全局事务成功,就清除日志;如果失败,就用日志里的数据反向恢复,实现自动回滚。
-
案例:反向海淘平台的订单全链路
一个叫Taocarts的反向海淘平台,用户下单后链路很长:支付、采购、仓储、物流等。在引入Seata AT模式前,数据不一致率高达8.2% 。
引入后,开发者只需在订单服务的入口方法上添加一个
@GlobalTransactional注解:java@GlobalTransactional public void createCrossBorderOrder(OrderDTO orderDTO) { // 1. 创建主订单 createOrder(orderDTO); // 2. 调用支付服务 payFeignClient.createPayOrder(order); // 3. 调用采购服务 purchaseFeignClient.createPurchaseTask(order); // 4. 调用仓储服务 wmsFeignClient.preEmptyStorage(order); }这样一来,上述四个跨服务调用就被Seata纳入了同一个全局事务。任何一个环节失败,所有已执行的操作都会自动回滚。最终,该平台的数据不一致率从8.2% 骤降至0.1%。
2. TCC 模式:手动的精细化方案 (Try-Confirm-Cancel)
这种模式需要你手动编写 三个阶段的代码,但能对每一个操作进行精细化控制。
-
核心思想:
- Try(尝试):检查和预留资源。
- Confirm(确认):真正执行业务。
- Cancel(取消):释放预留的资源(补偿操作)。
-
案例:机票预订
扣减库存,在TCC模式下可以设计为:
- Try :检查机票库存,并将目标库存冻结(状态改为"锁定中"),但不真正扣减。
- Confirm :将"锁定中"的库存真正扣减(状态改为"已售出")。
- Cancel :将"锁定中"的库存释放(状态改回"可售")。
这种方式通过冻结资源保证了数据一致性,避免了并发问题,但开发成本较高。
3. SAGA 模式:长流程的补偿方案
SAGA模式专为流程长、环节多 的复杂业务设计,通过补偿操作来撤销已成功的步骤。
-
核心思想 :将一个长事务拆分成一连串的本地事务,每个事务都有对应的补偿操作 。如果某个环节失败,就逆向执行之前所有环节的补偿操作。
-
案例:一次完整的旅行预订
假设预订一次旅行需要依次完成:订酒店、订机票、租车。
- 正向流程 :订酒店(成功)→ 订机票(成功)→ 租车(失败)。
- 补偿流程 :因为租车失败,SAGA会逆序 执行补偿:取消机票订单 → 取消酒店订单 。
最终,整个预订行为被取消,数据回归一致。
4. XA 模式:数据库级别的强一致性方案
XA模式是利用数据库自身支持的两阶段提交(2PC)协议 来实现的。它能保证强一致性 ,但性能开销大,不适用于高并发场景。它适合对一致性要求极高、并发不高的金融核心系统。
💎 总结
为了方便你对比和选择,我把这几种方案的特点整理成了下面这个表格:
| 特性 | 最终一致性 (消息队列) | Seata AT | Seata TCC | Seata SAGA | Seata XA |
|---|---|---|---|---|---|
| 核心思想 | 异步消息,确保最终成功 | 自动生成回滚日志 | 手动编码Try-Confirm-Cancel | 定义长事务与补偿操作 | 数据库原生两阶段提交(2PC) |
| 一致性 | 最终一致性 | 最终一致性 | 最终一致性 | 最终一致性 | 强一致性 |
| 性能 | 高(异步) | 高 | 较高 | 高(无锁) | 低(有锁) |
| 代码侵入 | 有(需实现消息表等) | 无 | 有(需实现三个方法) | 有(需定义补偿逻辑) | 无 |
| 适用场景 | 对实时性要求不高、需要解耦的业务 | 绝大多数微服务场景 | 核心业务,需精细控制资源 | 长流程、复杂的业务 | 金融等强一致性且并发不高的场景 |
简单来说:
- 追求快速、无侵入 地解决大多数问题,选 Seata AT。
- 业务流程极长、环节复杂 ,考虑 Seata SAGA。
- 对资源控制 有极致要求,选 Seata TCC。
- 需要强一致性 且能接受性能损失,选 Seata XA。
- 如果希望系统彻底解耦 ,能接受一定的开发量和延时,可以考虑消息队列方案。