微服务Seata 分布式事务详解与解析
在微服务架构中,一个业务操作往往需要调用多个不同服务的数据库,这就引入了跨服务的数据一致性问题,即分布式事务。Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。
一、Seata 概述
Seata 全称 Simple Extensible Autonomous Transaction Architecture,由阿里巴巴开源。它旨在解决微服务架构下的分布式事务问题,支持多种事务模式,能够确保全局数据一致性。
核心目标:对业务无侵入或低侵入,高性能,支持多种存储和事务模式。
二、Seata 核心概念
Seata 定义了三个核心角色:
- TC (Transaction Coordinator) - 事务协调器:维护全局事务和分支事务的状态,驱动全局事务的提交或回滚。通常独立部署(Seata Server)。
- TM (Transaction Manager) - 事务管理器:定义全局事务的边界,负责开启、提交或回滚一个全局事务。TM 向 TC 发起全局事务的开启、提交、回滚请求。
- RM (Resource Manager) - 资源管理器:管理分支事务上的资源,向 TC 注册分支事务,汇报分支事务状态,驱动分支事务的提交或回滚。通常对应一个微服务实例,管理该服务本地数据库的操作。
事务模型:
- 全局事务:由 TM 开启的整个分布式事务,包含一个或多个分支事务。
- 分支事务:每个 RM 本地执行的事务,是全局事务的组成部分。
三、Seata 整体架构
Seata 采用 TC 作为中心协调者,TM 和 RM 与 TC 通信。其基本工作流程如下:
- TM 向 TC 申请开启一个全局事务,TC 返回一个全局唯一的 XID。
- XID 在微服务调用链路上传播(通常通过拦截器植入 RPC 上下文)。
- 各个微服务的 RM 将本地事务注册到 TC 上,作为该全局事务的分支事务。
- TM 根据业务执行情况向 TC 发起全局提交或回滚。
- TC 驱动所有分支事务进行提交或回滚。
四、Seata 支持的事务模式详解
Seata 提供了四种事务模式:AT、TCC、Saga、XA。其中 AT 模式是其主推的、对业务无侵入的模式,其他模式用于特定场景。
1. AT 模式(Automatic Transaction)—— 自动补偿
AT 模式是 Seata 最具特色的模式,它基于本地 ACID 事务,通过两阶段提交(2PC)变种实现,但对业务代码几乎零侵入。
原理:
- 一阶段:业务数据和回滚日志在同一个本地事务中提交。在执行业务 SQL 之前,Seata 通过 JDBC 数据源代理拦截 SQL,解析 SQL 语义,生成“前镜像”(修改前的数据)和“后镜像”(修改后的数据),并将镜像数据组织成回滚日志记录(UNDO_LOG 表),与业务 SQL 一起提交。这一阶段直接提交了本地事务,释放了本地锁。
- 二阶段:
- 如果全局事务成功,TC 通知分支事务删除 UNDO_LOG,异步完成,无需额外开销。
- 如果全局事务失败,TC 通知分支事务回滚。RM 根据 UNDO_LOG 中的前镜像生成反向补偿 SQL,将数据恢复原样,然后删除 UNDO_LOG。
关键机制:
- 写隔离:AT 模式通过全局锁(由 TC 维护)保证不同全局事务对同一数据的写冲突。一阶段本地事务提交前,必须获取全局锁;获取失败则等待重试或回滚。全局锁在二阶段结束时释放(提交或回滚后)。
- 读隔离:在数据库本地隔离级别基础上,AT 模式默认不保证全局读已提交,但可以通过 SELECT FOR UPDATE 语句触发全局锁检测,实现读已提交。
优点:对业务代码几乎无侵入,自动生成补偿 SQL,开发效率高。
缺点:需要建立 UNDO_LOG 表,对数据库有一定要求(支持本地事务),性能损耗较 TCC 略高(因生成前后镜像)。
2. TCC 模式(Try-Confirm-Cancel)—— 手动补偿
TCC 是一种业务层面的补偿型事务,需要业务方实现 Try、Confirm、Cancel 三个接口。
三个阶段:
- Try:完成业务检查,预留资源(例如,冻结库存、预扣余额)。
- Confirm:真正执行业务,使用 Try 阶段预留的资源。Confirm 操作需保证幂等性。
- Cancel:释放 Try 阶段预留的资源,同样需保证幂等性。
流程:
- TM 发起全局事务,调用各参与方的 Try 接口。
- 如果所有 Try 成功,TM 调用各参与方的 Confirm 接口完成提交;否则调用 Cancel 接口进行回滚。
优点:性能较高(无需生成镜像,无全局锁),灵活性高,可自定义补偿逻辑。
缺点:对业务代码侵入性强,需自行实现幂等、空回滚、悬挂等复杂问题。
注意事项:
- 空回滚:当 Try 未执行,但 Cancel 被调用时(例如 RPC 超时,TM 判定失败直接 Cancel),Cancel 需能正确处理(识别事务状态,不做实际回滚)。
- 幂等:Confirm 和 Cancel 可能被重复调用,必须保证幂等性。
- 悬挂:指 Cancel 在 Try 之前执行,导致 Try 后续执行时资源被误释放。通常通过事务日志记录状态来避免。
3. Saga 模式
Saga 是一种长事务解决方案,将一个分布式事务拆分为一系列本地事务,每个本地事务都有对应的补偿操作。Saga 有两种实现方式:
- 状态机引擎:Seata 提供了基于状态机的 Saga 实现,通过 JSON/XML 定义业务流程和补偿流程,由引擎驱动执行。
- 命令协调:通过事件/命令的方式编排。
执行流程:
- 正常情况:按顺序执行每个本地事务,全部成功则事务结束。
- 异常情况:如果某个本地事务失败,则按照相反顺序依次调用之前已成功事务的补偿操作。
优点:适合长事务,性能较好,可通过状态机可视化编排,适合业务流程复杂的场景。
缺点:缺乏隔离性(提交即可见,无法回滚已提交的中间状态),需要业务方自行处理隔离问题;补偿逻辑复杂。
4. XA 模式
XA 模式是基于 XA/Open DTP 标准的分布式事务协议,由数据库本身支持。
原理:
- 一阶段:TM 通知所有 RM 预提交,RM 执行 XA 准备,将事务信息持久化,但数据对其它事务不可见(处于锁定状态)。
- 二阶段:如果所有 RM 预提交成功,TM 通知所有 RM 提交,否则通知回滚。
Seata 的 XA 模式:Seata 对 XA 做了封装,通过数据源代理自动管理 XA 事务,业务代码无需改动。
优点:对业务无侵入,强一致性,符合 ACID。
缺点:性能较差(资源锁定时间长,依赖于数据库的 XA 能力),不适合高并发场景。
五、AT 模式深度解析
因为 AT 模式是 Seata 的核心,我们深入理解其内部机制。
1. 全局锁
AT 模式为了保证写隔离,引入了全局锁机制。每个分支事务在提交本地事务前,必须向 TC 申请全局锁。全局锁的 key 通常由资源ID(库表)、行数据主键组成。TC 维护着全局锁的持有情况。如果全局锁已被其他全局事务持有,则当前事务必须等待或回滚。
全局锁的持有时间是从一阶段提交后到二阶段结束(提交或回滚)。这期间,该行数据对其它全局事务是“锁定”的,但对本地读(非全局事务)是可见的(取决于数据库隔离级别)。
2. 回滚日志(UNDO_LOG)
每个 RM 对应的数据库中需要建立 undo_log 表,用于存储一阶段生成的前后镜像。表结构包含分支事务ID、全局事务ID、前镜像、后镜像、回滚状态等字段。二阶段回滚时,Seata 会根据 undo_log 生成反向 SQL;提交时则删除日志。
3. 隔离级别
- 读未提交:AT 模式默认情况下,一阶段提交后数据即对其他本地事务可见,如果二阶段回滚,则出现脏读。因此,AT 模式默认的全局隔离级别是读未提交。
- 读已提交:如果需要全局读已提交,可以在业务查询中使用 SELECT FOR UPDATE,Seata 会通过代理拦截该语句,并尝试获取全局锁。如果该行被其他全局事务锁定且未提交,则当前查询会被阻塞,直到全局锁释放(即二阶段完成),从而避免了脏读。这相当于实现了读已提交隔离。
4. 性能考量
AT 模式的主要开销在于:SQL 解析、前后镜像生成、UNDO_LOG 写入、全局锁的申请与释放。相比直接本地事务,AT 模式会增加约 10%-30% 的性能损耗,但相比传统 2PC(如 XA)已经轻量很多。
六、Seata 部署与集成
1. Seata Server(TC)部署
Seata Server 支持多种存储模式(file、db、redis)和注册中心(nacos、eureka、consul、zk)。生产环境建议使用 db 模式存储事务日志,保证 TC 高可用。部署时需注意:
- 使用 MySQL 存储事务会话信息,需要建表(global_table、branch_table、lock_table)。
- 配置 registry.conf 指定注册中心和配置中心(如使用 Nacos)。
- 启动 Seata Server:
sh seata-server.sh。
2. 客户端集成(以 Spring Boot 为例)
- 引入依赖
spring-cloud-starter-alibaba-seata(Spring Cloud Alibaba 场景下)或seata-spring-boot-starter。 - 配置
application.yml中的 seata 部分:tx-service-group、服务分组、registry(与 TC 通信方式)。 - 启用数据源代理:使用
@EnableAutoDataSourceProxy或配置 Seata 数据源代理(自动配置已处理)。 - 在事务发起方使用
@GlobalTransactional注解标记全局事务边界。 - 确保业务表所在的数据库有
undo_log表(AT 模式)或业务方实现 TCC 接口。
七、实践中的注意事项
- 事务超时:
@GlobalTransactional可设置超时时间,防止长时间占用全局锁。 - 幂等性设计:在 TCC 或 Saga 模式中,Confirm/Cancel 需支持幂等,通常通过事务状态表记录执行状态。
- 空回滚与悬挂(TCC):使用事务日志和防悬挂表来规避。例如在 Try 之前记录事务状态,Cancel 时检查是否 Try 过。
- 性能调优:AT 模式下,避免大事务(大量数据修改),减少全局锁持有时间;批量操作可考虑分拆多个小事务。
- 监控与告警:Seata 提供简单的监控页面,也可对接 Prometheus + Grafana。关注 TC 的吞吐量、失败事务数等指标。
- 混合模式使用:不同场景使用不同模式,如核心交易用 TCC,非核心用 AT。
八、与其他分布式事务方案的对比
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| Seata AT | 无侵入,自动补偿,性能较好 | 需要 UNDO_LOG 表,全局锁可能引起冲突 | 大多数微服务场景,特别是对开发效率要求高的团队 |
| Seata TCC | 性能高,灵活控制资源 | 侵入性强,需处理空回滚/悬挂 | 高性能核心交易,资源预留场景 |
| Seata Saga | 适合长流程,可视化编排 | 隔离性需自行处理 | 业务流程复杂,对一致性要求稍低的场景 |
| Seata XA | 强一致,无侵入 | 性能差,依赖数据库 XA | 对一致性要求极高、并发较低的遗留系统改造 |
| LCN | 早期方案,简单 | 锁粒度大,性能差,已不再维护 | 不推荐新项目使用 |
| Hmily | TCC 实现,支持多种 RPC | 只支持 TCC,功能相对单一 | 仅需 TCC 模式的轻量级场景 |
| 本地消息表 | 可靠最终一致 | 需维护消息表,开发量大 | 对一致性要求不高,可接受延迟的场景 |
九、总结与选型建议
Seata 提供了丰富的分布式事务解决方案,覆盖了从强一致到最终一致的各种需求。在实际选型时,可以参考以下建议:
- 优先考虑 AT 模式:对于大部分业务场景,如果对性能不是极端敏感,AT 模式可以极大降低开发成本,且基本能满足一致性和隔离性要求。
- 核心资金链路用 TCC:对于涉及资金、库存等核心资源,需要精细控制资源预留和释放的场景,TCC 能提供更好的性能和灵活性。
- 长流程业务用 Saga:对于业务流程复杂、涉及多个服务且耗时较长的场景,Saga 状态机可以清晰定义补偿逻辑,避免长时间锁定资源。
- 严格强一致用 XA:只有在对一致性要求极为严格(如金融对账),且并发量不高,同时数据库支持 XA 时,才考虑 XA 模式。
Seata 仍在快速发展中,社区活跃,建议关注其最新版本特性,如对多注册中心的支持、性能优化等。合理使用 Seata,能有效解决微服务架构下的分布式事务难题。
更多推荐
所有评论(0)