文章

Seata 面试题

Seata 面试题

以下内容,部分整理自 Seata 官网

Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 为用户提供了 ATTCCSAGAXA 事务模式,为用户打造一站式的分布式解决方案。

Seata 简介

Seata 是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务。

  • 官网: seata.io
  • GitHub: github.com/seata/seata (超过 20k stars)
  • 核心功能: 提供 ATTCCSAGAXA 四种事务模式。
  • 背景: 在开源之前,Seata 是阿里巴巴内部的分布式一致性中间件,成功支撑了历年“双十一”等复杂场景。其商业化产品为阿里云上的 GTS

相关链接:

Seata 的三大核心模块

Seata 的架构主要由三个核心角色组成:

  1. TC (Transaction Coordinator) - 事务协调者
    • 职责:作为中心化的服务端,负责生成全局唯一的事务 ID,对全局事务进行注册、提交或回滚。
  2. TM (Transaction Manager) - 事务管理器
    • 职责:作为客户端 SDK 嵌入在事务发起方应用中,负责定义全局事务的边界(开始、提交、回滚)。
  3. RM (Resource Manager) - 资源管理器
    • 职责:作为客户端 SDK 嵌入在参与事务的各个资源服务中,负责管理分支事务,并与 TC 通信以汇报状态和执行指令。

在 Seata 中,TMRM 作为客户端与业务系统集成,而 TC 是一个独立部署的服务端。

Seata 分布式事务的执行流程

Seata 的分布式事务遵循一个标准的二阶段提交流程:

  1. TM 请求 TC 开启一个全局事务,TC 生成并返回一个全局事务 ID (XID)。
  2. 各个 RM 在执行本地业务逻辑时,向 TC 注册分支事务,并汇报资源准备状态。
  3. TM 在业务逻辑执行完毕后,通知 TC 结束全局事务(触发全局提交或回滚),这是一阶段的结束。
  4. TC 汇总所有分支事务的状态,决定全局事务是提交还是回滚。
  5. TC 通知所有 RM 执行最终的提交或回滚操作,这是二阶段的结束。

Seata 事务流程

这个模型与经典的 X/Open DTP (Distributed Transaction Process) 模型非常相似。

DTP 模型角色:

  • AP (Application): 应用程序,定义事务边界。
  • TM (Transaction Manager): 事务管理器,协调全局事务。
  • RM (Resource Manager): 资源管理器,通常是数据库。

DTP 模型

Seata 的四种事务模式

Seata 提供了四种不同的分布式事务解决方案以适应不同场景:

  • AT (Automatic Transaction) 模式
  • TCC (Try-Confirm-Cancel) 模式
  • Saga 模式
  • XA 模式

Seata AT 模式详解

AT (Automatic Transaction) 模式 是 Seata 最早支持且最常用的模式,它是一种基于增强型两阶段提交协议的演进方案,核心优势在于对业务代码的侵入性极低。

AT 模式的使用前提

  1. 基于支持本地 ACID 事务的关系型数据库 (例如,使用 InnoDB 存储引擎的 MySQL)。
  2. Java 应用,通过 JDBC 访问数据库。

AT 模式核心机制

AT 模式是对传统两阶段提交(2PC)的优化:

  • 一阶段:业务数据和回滚日志(undo_log)在同一个本地事务中提交,然后立即释放本地锁和数据库连接资源。
  • 二阶段
    • 如果全局提交TC 会异步通知各 RM 清理对应的 undo_log,这个过程非常快。
    • 如果全局回滚RM 会根据一阶段记录的 undo_log 生成反向补偿操作来恢复数据。

Seata AT 模型图

AT 模式工作流程示例(充值业务)

假设一个充值业务需要调用“余额服务”和“积分服务”。

第一阶段流程

第一阶段流程图

  1. 余额服务的 TMTC 申请开启全局事务,获取 XID。
  2. 余额服务的 RMTC 注册分支事务。
  3. 余额服务在本地事务中执行业务 SQL,并生成 undo_log,然后提交本地事务。
  4. 余额服务的 RMTC 汇报分支事务执行成功。
  5. 余额服务通过 RPC 调用积分服务,并传递 XID。
  6. 积分服务的 RMTC 注册分支事务。
  7. 积分服务在本地事务中执行业务 SQL,并生成 undo_log,然后提交本地事务。
  8. 积分服务的 RMTC 汇报分支事务执行成功。
  9. 积分服务返回调用成功。
  10. 余额服务的 TMTC 请求全局提交。

第二阶段流程

  • 全局提交TC 异步通知所有 RM 删除对应的 undo_log 记录。
  • 全局回滚TC 通知所有 RM 根据 undo_log 进行数据回滚。回滚前,RM 会校验当前数据与 undo_log 中的“后镜像 (afterImage)”是否一致,以防止数据被其他事务修改(避免脏写)。

Seata AT 模式在电商下单场景的应用

假设一个电商下单场景:shopping-service 调用 repo-service 扣减库存,然后调用 order-service 创建订单。

电商场景

工作流程详述

  1. shopping-service (TM) 向 TC 注册全局事务,获取 XID。
  2. repo-service (RM) 执行本地事务:
    • 锁定库存记录。
    • 生成 undo_log(记录数据修改前后的镜像)。
    • 执行 UPDATE 语句扣减库存。
    • 提交本地事务,释放本地锁。
    • TC 注册分支事务并汇报成功。
  3. order-service (RM) 执行本地事务:
    • 锁定订单表。
    • 生成 undo_log
    • 执行 INSERT 语句创建订单。
    • 提交本地事务,释放本地锁。
    • TC 注册分支事务并汇报成功。
  4. TC 收到所有分支事务成功的信息,决定全局提交。
  5. TC 通知所有 RM 异步删除 undo_log

如果任何一个分支事务失败,TC 将决定全局回滚,并通知所有 RM 根据 undo_log 恢复数据。

undo_log 表结构

undo_logAT 模式实现自动回滚的关键。

undo_log 表结构

  • xid: 全局事务 ID。
  • branch_id: 分支事务 ID。
  • rollback_info: 记录了数据修改前后的镜像(beforeImageafterImage)。

Seata 的数据隔离性

写隔离

AT 模式通过 全局锁 (Global Lock) 来保证写隔离。

  • 流程
    1. 一个分支事务在执行本地事务前,必须先获取本地锁。
    2. 在提交本地事务前,必须成功获取该记录的 全局锁
    3. 如果获取 全局锁 失败,则回滚本地事务,释放本地锁,并进行重试。
    4. 全局事务结束后,释放 全局锁
  • 结论:由于 全局锁 的存在,一个全局事务在完成之前会一直持有锁,从而阻止其他事务对同一记录的修改,避免了“脏写”问题。

写隔离-成功流程

写隔离-回滚流程

读隔离

  • 默认隔离级别:在数据库本地事务隔离级别为“读已提交” (Read Committed) 的基础上,Seata AT 模式默认的全局隔离级别是 “读未提交” (Read Uncommitted)。这意味着一个事务可能读取到另一个尚未提交的全局事务修改的数据。

  • 如何实现“读已提交”:如果业务需要全局的“读已提交”,可以使用 SELECT ... FOR UPDATE 语句。

    • 当执行 SELECT FOR UPDATE 时,Seata 的 RM 会尝试获取该记录的 全局锁
    • 如果 全局锁 被其他事务持有,查询将被阻塞,直到获取到锁为止。
    • 这样可以确保查询操作读取到的是已提交的数据。

读隔离

Spring Cloud 集成 Seata AT 模式

集成步骤非常简单:

  1. 在项目中引入 Seata 依赖,并配置 Seata Server 地址。
  2. 在每个参与事务的微服务数据库中创建 undo_log 表。
  3. 在事务发起方(主服务)的方法上添加 @GlobalTransactional 注解。
  4. 确保数据源被 Seata 的 DataSourceProxy 代理。

Seata TCC 模式

TCC (Try-Confirm-Cancel) 模式是另一种两阶段事务模型,与 AT 模式相比:

  • 优点:性能更高,不依赖数据库的本地事务,不使用 全局锁,允许更高的并发。
  • 缺点:对业务代码侵入更强,需要为每个分支事务手动实现 TryConfirmCancel 三个方法。

TCC 模式

TCC 的三个阶段

  1. 一阶段:Try
    • 对业务资源进行检查和预留。例如,在扣减账户余额时,不是直接扣减,而是将一部分金额“冻结”。
  2. 二阶段:Confirm
    • 如果全局事务提交,则执行真正的业务操作。例如,将“冻结”的金额实际扣除。
    • Confirm 方法必须是幂等的。
  3. 二阶段:Cancel
    • 如果全局事务回滚,则取消一阶段预留的资源。例如,将“冻结”的金额“解冻”。
    • Cancel 方法必须是幂等的。

TCC 流程


Seata Saga 模式

Saga 模式是 SEATA 提供的长事务解决方案。它将一个长事务拆分为多个本地事务,每个本地事务都有一个对应的补偿操作。

  • 正向执行:依次执行每个本地事务。
  • 反向补偿:如果某个本地事务失败,则依次调用前面已成功事务的补偿操作。

Saga 模式

适用场景

  • 业务流程长、涉及多个参与者。
  • 参与者可能是遗留系统或外部服务,无法提供 TCC 模式所需的接口。

优缺点

  • 优点
    • 一阶段提交本地事务,无锁,性能高。
    • 事件驱动,可异步执行,吞吐量高。
    • 补偿服务易于实现。
  • 缺点
    • 不保证隔离性。

Seata 通过内置的状态机引擎来实现 Saga 模式,开发者可以通过 JSON 格式的状态图来编排服务调用和补偿流程。


Seata XA 模式

XA 模式是 Seata 对传统 XA 规范的支持。它利用数据库本身对 XA 协议的支持来管理分支事务。

使用前提

  • 数据库必须支持 XA 事务(如 MySQL、Oracle)。
  • Java 应用通过 JDBC 访问数据库。

工作机制

XA 模式将 Seata 的 TC 作为 XA 规范中的“事务协调者”,而由数据库驱动实现的 XA 资源则作为“资源管理器”。

XA 模式机制

在 Seata 中使用 XA 模式,开发者只需将数据源代理从 DataSourceProxy (AT 模式) 更换为 DataSourceProxyXA (XA 模式) 即可,上层编程模型保持不变。

```java @Bean(“dataSource”) public DataSource dataSource(DruidDataSource druidDataSource) { // AT 模式的数据源代理 // return new DataSourceProxy(druidDataSource);

1
2
// XA 模式的数据源代理
return new DataSourceProxyXA(druidDataSource); }
本文由作者按照 CC BY 4.0 进行授权