跳到主要内容

分布式事务之 Seata

· 阅读需 6 分钟

在企业应用开发中,随着分布式框架的发展,生产环境里会有很多数据库实例。特别是在微服务领域,我们通常会让每个业务 Service 模块对应一个自身业务的 DB 存储节点,再对这个存储节点做高可用部署。

事务问题​

编写 Service 业务逻辑时,一个业务操作往往会远程调用其他业务模块,产生的数据会落盘到不同的存储节点。为了保障多个数据库实例之间事务的 ACID 特性,就遇到了分布式事务问题。

分布式事务要做到的事情可以概括为两条:

1)如果整个业务调用链路均成功,那么整个调用链路对应的数据库做事务提交。

2)如果调用链路中抛出了异常,那么整个调用链路对应的数据库做回滚操作。

Seata 的角色划分​

在 Seata 分布式事务解决方案中,一般有以下这些角色:

  • TC (Transaction Coordinator):事务协调者。
  • TM (Transaction Manager):事务管理器,在 AP 上进行集成。
  • RM (Resource Manager):资源管理器,管理分支事务处理的资源,与 TC 交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。
  • AP (Application Program):访问 RM 的应用程序。

图中的 TC 事务协调者组件需要单独部署,并加入到整个微服务集群的注册中心中,目前常见的注册中心均支持,如 Nacos、Zookeeper、Etcd、Eureka、Consul,同时需要在 AP 中做对应的连接配置。TC 模块必须保障高可用,如果该模块宕机,分布式事务将无从谈起。

事务分组与高可用

刚性事务与柔性事务​

刚性事务:通常无业务改造,强一致性,原生支持回滚/隔离性,低并发,适合短事务。对应方案:XA 协议(2PC、JTA、JTS)、3PC。

柔性事务:有业务改造,最终一致性,需要实现补偿接口与资源锁定接口,高并发,适合长事务。对应方案:TCC/FMT、Saga(状态机模式、Aop 模式)、本地事务消息、消息事务(半消息)。

那什么是长事务,什么是短事务呢?

长事务:长时间占用某个资源(X 锁、间隙锁、表锁、页锁),修改的数据较多或执行速度较慢,业务流程长。其他事务访问该资源时会被阻塞等待,容易导致死锁。

短事务:对资源的占用很短暂,修改的数据较少或执行很快完成,业务流程短,事务很快结束,产生死锁的概率较小。

四种事务模式​

目前 Seata 支持 4 种分布式事务的管理模式。

AT 模式​

AT 模式算是 Seata 中最具特色的模式,总的来说是优化过的 2PC,属于二阶段提交的一种。使用这种模式需要在本地数据库节点中创建一张 undolog 表,通过代理 DataSourceProxy 拦截应用程序执行的 DML SQL 语句,对 SQL 做语义解析并转化为查询 SQL,把执行前对应的数据镜像存入 undolog 表,作为二阶段回滚使用。以上步骤均由代理模式完成,所以实现了零业务逻辑代码入侵。

但此方案并非完美,性能损失相当大:一次 DML SQL 会多出一次语义转化并执行查询 SQL,以及新增数据镜像到 undolog 表;如果发生二阶段回滚,还需要获取本地锁,再回退原始数据镜像。此外还有一个比较严重的问题:如果在回滚时,其他本地事务没有走 Seata 的全局事务管理去获取全局锁,而是直接通过本地事务修改了 undolog 中某条数据镜像对应的数据,那么 Seata 回滚时会发现回滚镜像与当前本地数据对不上,出现数据丢失问题,有点类似于 CAS 中的 ABA 问题。

TCC 模式​

TCC 模式较为简单,其实也是 2PC 两阶段提交。它和 AT 模式的区别是需要自己实现一阶段 prepare 准备、二阶段 commit 以及 rollback 逻辑,Seata 的事务管理器会依次调用这些实现方法。

Saga 模式​

Saga 由一系列本地事务构成。每一个本地事务在更新完数据库之后,会发布一条消息或一个事件,触发 Saga 中下一个本地事务的执行。如果某个本地事务因业务规则无法满足而失败,Saga 会对这个失败事务之前已成功提交的所有事务执行补偿操作。

所以 Saga 算是实现起来最繁琐的一种模式,但基于 Saga 的状态机可以非常灵活地控制每一个分布式事务步骤。对于长事务场景,更需要自己细粒度地控制每一种情况:不关注整个分布式事务的状态,只需要做好每个事件的处理,以及自身方法的幂等性即可。但它的事务没有阶段提交一说,都是本地直接提交。如果在回滚途中,其他事务读取并修改了这些数据,就会发生脏读、脏写。对于这种情况,我想还需要自己实现一套 txid 方案来规避。

XA 模式​

XA 模式首先要求数据库支持 XA 协议,其实现有 2PC、3PC,且完全由数据库自身提供,应用程序不需要做任何处理。本质上和 AT 模式很相似,区别是数据库在执行 XA 事务的过程中,为了强一致性会持续占用数据资源(X 锁、间隙锁、表锁、页锁,视数据范围而定)。如果是长事务,在高并发场景下非常容易出现死锁,数据库性能也会下降得比较厉害。如果都是短事务,则可以使用此模式。

小结​

分布式事务的本质是让跨库调用链路要么一起提交、要么一起回滚。Seata 通过 TC、TM、RM 的角色分工把这件事产品化,并提供了 AT、TCC、Saga、XA 四种模式。AT 靠数据镜像做到零入侵,适合大多数常规场景;TCC 和 Saga 把补偿逻辑交给业务,换取更高的并发与灵活性;XA 依赖数据库原生支持,只适合短事务。选型时先分清业务是长事务还是短事务、能接受强一致还是最终一致,再对号入座。

评论 / COMMENTS