Skip to Content

功能

  • 为微服务架构提供可靠的分布式事务解决方案,解决跨服务的数据一致性问题

实现

Seata 实现分布式事务的核心机制在于集中式的事务协调器 (Transaction Coordinator, TC)基于代理的事务管理器 (Transaction Manager, TM)资源管理器 (Resource Manager, RM) 的协同工作。 为了更清晰地解释 Seata 的实现原理,我们可以从 Seata 的核心组件和其主要支持的 AT 模式入手,逐步展开:

Seata 的核心组件:

在深入了解 Seata 如何实现分布式事务之前,先了解 Seata 的三大核心组件至关重要:

  1. Transaction Coordinator (TC): 事务协调器

    • 定位: 独立部署的 Server 端,是 Seata 的核心大脑。
    • 作用:
      • 事务会话管理: 接收 TM 开启的全局事务请求,生成全局唯一的 XID (Global Transaction ID),并在整个分布式事务生命周期内维护事务会话的状态。
      • 协调事务分支: 接收 RM 注册的分支事务,管理分支事务的状态。
      • 驱动全局提交与回滚: 根据 TM 的指令,协调 RM 提交或回滚分支事务,最终决定全局事务的结果。
      • 状态持久化: TC 会将事务会话的状态持久化到存储介质中(如数据库),以保证即使 TC 发生故障重启,事务状态也不会丢失。
  2. Transaction Manager (TM): 事务管理器

    • 定位: 集成在微服务应用中的 Client 端组件。
    • 作用:
      • 全局事务的发起者: 负责开启、提交或回滚全局事务。
      • 与 TC 交互: 与 TC 通信,告知 TC 全局事务的开始、结束等状态变更。
      • 定义事务边界: 通过注解或 API 方式,在业务代码中定义分布式事务的边界。
  3. Resource Manager (RM): 资源管理器

    • 定位: 集成在微服务应用中的 Client 端组件,通常与数据库客户端在同一个进程中。
    • 作用:
      • 资源代理: 代理应用对底层数据源的操作,例如数据库连接。
      • 分支事务管理: 负责分支事务的注册、状态汇报。
      • 本地事务操作: 执行本地事务,并根据 TC 的指令进行分支事务的提交或回滚。
      • 与 TC 交互: 与 TC 通信,注册分支事务,报告分支事务状态,并接收 TC 的协调指令。

Seata AT 模式实现分布式事务的原理 (以数据库为例):

AT 模式 (Automatic Transaction Mode) 是 Seata 推荐使用的,对业务代码侵入性最小的事务模式。 其核心思想是 两阶段提交协议 (Two-Phase Commit, 2PC) 的改进版本。 AT 模式在一阶段就直接提交本地事务,二阶段再根据全局事务的最终结果进行补偿。 为了实现回滚,AT 模式引入了 Undo Log (回滚日志) 机制。

AT 模式的核心步骤:

  1. Begin (开始全局事务):

    • TM 向 TC 发起全局事务开启请求。
    • TC 创建全局事务会话,生成全局唯一的 XID (Global Transaction ID),并返回给 TM。
    • TM 将 XID 在微服务调用链路中传播 (例如通过 Dubbo 或 Spring Cloud Feign)。
  2. Execute (执行业务逻辑):

    • 参与全局事务的微服务在执行业务逻辑时,RM 会代理数据源连接。
    • 本地事务提交: 在执行 SQL 操作时,RM 会在本地事务中执行,并直接提交本地事务
    • Undo Log 记录: 在本地事务提交之前,RM 会自动记录 Undo Log,用于记录 SQL 操作的反向操作 (例如,INSERT 对应 DELETE,UPDATE 记录更新前后的数据)。 Undo Log 与业务数据在同一个数据库中,但通常存储在单独的表中。
    • 获取全局锁 (Global Lock): 在本地事务提交之前,RM 会尝试获取 全局锁。 全局锁的目的是在二阶段回滚时, 防止其他事务修改已提交但可能需要回滚的数据,保证数据一致性。
  3. Commit/Rollback (全局事务提交/回滚):

    • Commit: 如果整个分布式事务中的所有分支事务都执行成功,TM 向 TC 发起全局事务提交请求。

      • TC 接收到全局提交请求后,向所有参与的分支事务发送 Commit 指令
      • RM 接收到 Commit 指令后,异步删除 Undo Log。 由于本地事务已经提交,Commit 阶段的操作非常轻量级,性能很高。
    • Rollback: 如果分布式事务中任何一个分支事务执行失败,或者 TM 显式发起全局回滚请求,TM 向 TC 发起全局事务回滚请求。

      • TC 接收到全局回滚请求后,向所有参与的分支事务发送 Rollback 指令
      • RM 接收到 Rollback 指令后,根据 XID 和分支 ID 找到对应的 Undo Log
      • 执行 Undo Log 中的反向 SQL 操作,回滚之前本地事务的变更。
      • 释放全局锁

AT 模式的核心机制总结:

  • Undo Log (回滚日志): AT 模式的核心,记录 SQL 操作的反向操作,用于实现回滚。 保证了在本地事务提交后,仍能回滚数据的能力。
  • 全局锁 (Global Lock): 保证了在二阶段回滚时的数据隔离性,防止脏写问题。
  • 两阶段提交协议 (2PC) 的改进: AT 模式是对 2PC 的改进,一阶段提交本地事务,二阶段异步提交或回滚,提升了性能。
  • 事务协调器 (TC): 集中管理全局事务,协调分支事务的提交和回滚。

Q&A

[!question] 如何控制哪些服务参与全局事务管理?

控制哪些服务参与 Seata 的全局事务管理,本质上是控制全局事务的边界。 你需要明确哪些操作需要被纳入同一个全局事务中,而哪些操作可以独立于全局事务之外。 Seata 提供了多种方式来精细地控制哪些服务和操作参与全局事务管理,主要可以从以下几个层面来理解和实践:

1. 基于注解 @GlobalTransactional@Transactional 的方法级别控制 (最常用)

这是最常用且推荐的方式,它提供了方法级别的细粒度控制,允许你精确地指定哪些方法需要开启全局事务,哪些方法参与已有的全局事务,哪些方法不需要参与事务。

  • @GlobalTransactional 注解: 用于发起一个全局事务。 通常加在服务入口方法或者业务逻辑的顶层方法上。 被 @GlobalTransactional 注解的方法执行时,Seata 会创建一个新的全局事务,并生成全局唯一的 XID。 这个 XID 会在后续的微服务调用链中传播。
    import io.seata.spring.annotation.GlobalTransactional; import org.springframework.stereotype.Service; @Service public class OrderServiceImpl implements OrderService { @GlobalTransactional(rollbackFor = Exception.class) // 开启全局事务 public String createOrder(Order order) { // ... 订单服务逻辑 ... inventoryService.reduceInventory(order.getProductId(), order.getQuantity()); // 调用库存服务 accountService.扣减账户余额(order.getAccountId(), order.getAmount()); // 调用账户服务 // ... 其他操作 ... return "订单创建成功"; } }
在上面的例子中,`createOrder` 方法被 `@GlobalTransactional` 注解,这意味着当调用 `createOrder` 方法时,Seata 会开启一个全局事务。 `inventoryService.reduceInventory` 和 `accountService.扣减账户余额` 的调用都会自动加入到这个全局事务中。 - **`@Transactional` 注解 (Spring 的事务注解):** 用于**加入**一个已存在的全局事务。 当一个被 `@Transactional` 注解的方法被调用时,Seata 会检查当前上下文中是否已经存在全局事务 (通常是由于上游服务通过 `@GlobalTransactional` 发起的)。 如果存在,则当前方法的操作会加入到已有的全局事务中,成为一个分支事务。 如果不存在全局事务,则 `@Transactional` 注解的行为与 Spring 原生的本地事务行为一致 (如果没有配置事务管理器,则可能没有事务效果)。 ``` Java import io.seata.spring.annotation.GlobalTransactional; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; // 引入 Spring 的事务注解 @Service public class InventoryServiceImpl implements InventoryService { @Transactional // 加入已有的全局事务 (如果有) public void reduceInventory(Long productId, Integer quantity) { // ... 库存服务逻辑 ... // 执行库存扣减的数据库操作 } } ``` 在上面的例子中,`reduceInventory` 方法被 `@Transactional` 注解。 当 `OrderServiceImpl.createOrder` 方法 (被 `@GlobalTransactional` 注解) 调用 `inventoryService.reduceInventory` 时,`reduceInventory` 方法会自动加入到 `createOrder` 方法发起的全局事务中。 **总结方法级别控制的关键点:** - **发起者:`@GlobalTransactional`**: 只有被 `@GlobalTransactional` 注解的方法才会真正发起全局事务。 - **参与者:`@Transactional`**: 被 `@Transactional` 注解的方法,如果在全局事务上下文中被调用,则会作为分支事务参与。   - **默认不参与**: 默认情况下,没有添加任何事务注解的服务方法,不会主动参与到全局事务中。 它们的操作将独立于全局事务之外。 - **灵活控制**: 通过在不同的方法上选择性地添加 `@GlobalTransactional` 或 `@Transactional` 注解,你可以非常灵活地控制哪些服务和操作参与全局事务管理。
Last updated on