1.3 分布式面试 什么是“最终一致性”?

这两个概念是微服务架构中处理分布式事务的核心。简单来说:

  • 最终一致性 :是一种设计思想,为了系统可用性,允许数据短暂不一致,但保证最终会一致。
  • Seata :是一个具体工具,它用"最终一致性"等思想,帮你实现这种效果。

🤔 什么是"最终一致性"?

要理解它,得先看一个更根本的问题:为什么在微服务里,保证数据一致这么难?

问题根源:从本地事务到分布式事务

在传统单体应用中,多个数据操作(如扣库存、生成订单)可以在同一个数据库事务(ACID)里完成,要么全成功,要么全失败。

但在微服务里,一个业务操作(比如下单)可能涉及订单服务支付服务库存服务 等多个独立服务,每个服务都有自己的数据库。要让这些跨服务的操作保持原子性,就变成了一个分布式事务问题。如果缺乏协调,就可能出现"钱扣了,但订单没生成"这种数据不一致的严重故障。

CAP定理与BASE理论

为什么分布式事务这么难?因为分布式系统受限于CAP定理

  • C(一致性):所有节点数据随时一致。
  • A(可用性):服务始终可用。
  • P(分区容错性):网络故障时系统仍能工作。

在网络故障(P)必然发生时,你必须在一致性(C)可用性(A) 之间做权衡。

为了保证高可用(A) ,业界提出了BASE理论

  • 基本可用(Basically Available):允许系统部分功能受损。
  • 软状态(Soft State):允许数据中间状态,可以暂时不一致。
  • 最终一致性(Eventually Consistent):系统保证经过一段时间后,数据最终会达到一致状态。

这就是最终一致性 的核心:用"暂时的不一致"换取"系统的高可用"

🛠️ Seata 是什么?怎么解决?

Seata 是阿里巴巴开源的分布式事务解决方案。它就像一个全局的"事务协调员",把多个微服务的本地事务串联成一个全局事务

Seata的核心组件

Seata通过三个核心角色协同工作:

  1. 事务协调者 (TC):独立部署的中央服务器,负责协调和管理所有全局事务的状态。
  2. 事务管理器 ™:嵌入在发起全局事务的服务(如订单服务)中,负责开启、提交或回滚全局事务。
  3. 资源管理器 (RM):嵌入在参与事务的每个服务中,负责执行本地事务,并向TC注册和报告状态。
Seata的四种事务模式

Seata提供了四种模式,其中后三种都基于最终一致性思想。

模式 一致性 业务侵入 核心思想 适用场景
XA模式 强一致性 无侵入 基于数据库的两阶段提交(2PC),在准备阶段锁定资源,所有参与者就绪后统一提交或回滚。 对数据一致性要求极高、能接受性能损耗的场景。
AT模式 最终一致性 无侵入 Seata的默认模式 。通过解析SQL,自动生成并记录回滚日志(Undo Log)。业务提交后,若需回滚则用日志恢复数据。 大多数标准微服务场景,希望无侵入地解决分布式事务问题。
TCC模式 最终一致性 有侵入 业务代码需实现 Try(预留资源)、Confirm(确认执行)、Cancel(补偿回滚) 三个接口。 核心业务 ,对性能要求高,需要精细控制资源锁定粒度。
SAGA模式 最终一致性 有侵入 将长事务拆分为一系列本地事务 ,每个事务有对应的补偿操作。失败时,逆序执行补偿。 长事务、业务流程复杂、参与者可能不是数据库的遗留系统。

💎 总结

  • 最终一致性 :是一种设计哲学,指导我们在分布式系统中,为了高可用性,可以接受数据的短暂不一致。
  • Seata :是一个实用工具 ,它提供了AT、TCC等多种模式,帮我们把"最终一致性"这个思想落地,以不同的方式管理跨服务的数据一致性。
    好的,我们接着上次的话题,用一个具体的电商下单场景,把"最终一致性"和Seata的四种模式掰开揉碎了讲清楚。

想象一下,我们正在搭建一个微服务架构的电商系统。用户下单这个看似简单的动作,背后需要订单服务库存服务****和账户服务**协同完成。

🚀 从下单场景看分布式事务

这个场景的核心流程是:

  1. 订单服务创建一条"待支付"的订单。
  2. 库存服务扣减相应商品的库存。
  3. 账户服务扣减用户的账户余额。

问题在于,这三个操作分别由三个独立的服务(和三个独立的数据库)完成。如何保证它们要么全部成功 ,要么全部失败,就是分布式事务要解决的核心问题。

💡 方案一:"最终一致性"的异步消息方案

"最终一致性"是一种务实的策略,它允许数据在短时间内不一致,但承诺最终会达成一致。

核心思想 :订单服务创建订单后,不直接调用库存和账户服务,而是发个消息到消息队列(MQ),让下游服务自己去处理。

案例:本地消息表 + RocketMQ

  1. 业务执行与记录消息(在一个本地事务里) :订单服务在同一个数据库事务中,同时做两件事:

    • 插入一条订单数据。
    • 向一张local_message表里,插入一条"待发送"的消息,内容为"订单已创建,请扣减库存和余额"。
  2. 可靠消息投递 :一个后台定时任务,会扫描local_message表,将"待发送"的消息投递到RocketMQ。如果投递失败,会重试,直到成功。

  3. 下游服务消费:库存服务和账户服务各自订阅并消费这条消息,执行本地的扣减操作。

最终一致性体现 :在订单创建和消息发送之间,或消息处理过程中,数据是不一致的。但通过消息重试幂等性设计 (保证重复消息不会导致重复扣减),系统能保证所有操作最终都会成功,数据最终一致。这个方案性能好、解耦,但开发量较大,且实时性稍弱。

🛠️ 方案二: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
  • 如果希望系统彻底解耦 ,能接受一定的开发量和延时,可以考虑消息队列方案。