Seata — AT模式和TCC模式详细对比
深入对比分析了 AT 和 TCC 模式在 网络 I/O 和 锁资源 两个方面的性能差异,并根据分析结果提供了选择建议。最后,讨论了 AT 模式在业务中使用不当可能带来的问题,并提出了相应的解决方案
前置文章
性能
首先要明确一点,当数据量不大的时候,纯内存计算是受本地硬件的限制,掌握在自己手里的东西都算是可控的,所以性能问题不需要特别担心。况且内存操作本身非常快,更不用在性能分析里占用太多关注。所以业务运行效率的主要瓶颈还是 网络 I/O 和 锁资源开销。网络性能会受到很多不可控因素影响,比如带宽、延迟、网络波动、CDN 状况,甚至对方的运行效率。而锁资源往往存在竞争,通常会导致并行处理受阻。所以,评估业务性能时,网络 I/O 和锁资源开销才是最值得关注的两个点。我们也从这两个点切入对比分析AT和TCC模式下非业务部分的性能损耗
网络IO
一阶段
AT
- 开启全局事务和注册分支都通过 RPC 与 TC 通信
- 每条非SELECT的DML都会生成前后镜像 SQL,并进行查询
- 分支事务的undo_log数据落库
TCC:一阶段即try阶段,仅有开启全局事务和注册分支事务会和TC进行PRC通信
二阶段
- AT
- commit:仅同步删除lock_table,其余都异步完成,很快
- rollback:依次同步,TC会和每个分支事务通信。每个分支事务需要查询自己的 undo_log,并且查询最新的数据与 undo_log 中的数据进行对比,接着,生成补偿 SQL 来执行回滚
- TCC
- commit:依次同步,TC会和每个分支事务通信,都操作自己的commit方法
- rollback::依次同步,TC会和每个分支事务通信,都操作自己的rollback方法
锁
- AT:在一阶段注册分支事务时,TC 会为每个分支事务构建行锁,并进行独占锁定(基于 xid)。如果发生锁冲突,可能会触发多次重试,直到获得锁或超时
- TCC:try 阶段仅仅是预留资源,并不会直接操作最终资源,因此不存在锁竞争问题
总结
可以看出,在全局事务的 commit 阶段,AT 确实比 TCC 更快,但在其他阶段,AT 的性能通常较差。AT 需要大量构建数据镜像和准备 undo_log,而回滚时还需将 undo_log 与最新数据对比,确保数据没有被其他非全局事务并发修改。最重要的是,AT 存在锁竞争问题,在整个全局事务期间会阻止其他全局事务并发操作数据。如果有非全局事务操作数据,还需要使用 @GlobalLock 来防止并发修改。
因此,从整体来看,AT 的性能通常要比 TCC 差的多
业务侵入性
AT 模式几乎没有业务侵入,只需通过一个 @GlobalTransactional 注解即可完成事务管理。
而 TCC 模式则相对复杂,除了需要在数据库层增加资源预扣减字段,还需要在代码中手动实现完整的 try、commit 和 rollback 逻辑
因此,从业务侵入性和使用难易度来看,AT 完胜 TCC
选择建议
AT 适用于流程简单、业务逻辑较直观且 SQL 操作不复杂的场景,尤其是当事务的回滚操作主要依赖于自动生成的 undo_log 时。AT 模式适合对性能要求不高的业务,因为复杂的 SQL 可能会影响 undo_log 的构建和性能。
而 TCC 更适合那些复杂、对性能要求较高的场景,特别是当需要对每个参与者的操作进行精确控制时。例如,在涉及多个服务的核心业务中,TCC 可以确保每个事务阶段的行为都能被灵活处理和回滚
使用提醒
在使用 AT 模式时,建议将 @GlobalTransactional 和 @Transactional 配合使用。单独使用 @GlobalTransactional 虽然也能实现分布式事务的管理,但每个 SQL 操作都会创建一个新的分支事务,导致频繁的与 TC 进行 RPC 通信,这会显著增加性能开销
此外,全局事务 的发起者是控制全局事务回滚的核心。如果下游分支事务发生问题导致本地回滚,必须确保最上游的全局事务发起者能够感知到异常,才能触发全局事务的完整回滚,避免数据不一致