开发者说 | 分布式事务中间件 Seata 的设计原理

开源君
• 阅读 144

导读

微服务架构体系下,我们可以按照业务模块分层设计,单独部署,减轻了服务部署压力,也解耦了业务的耦合,避免了应用逐渐变成一个庞然怪物,从而可以轻松扩展,在某些服务出现故障时也不会影响其它服务的正常运行。总之,微服务在业务的高速发展中带给我们越来越多的优势,但是微服务并不是十全十美,因此不能盲目过度滥用,它有很多不足,而且会给系统带来一定的复杂度,其中伴随而来的分布式事务问题,是微服务架构体系下必然需要处理的一个痛点,也是业界一直关注的一个领域,因此也出现了诸如 CAP 和 BASE 等理论。

在今年年初,阿里开源了一个分布式事务中间件,起初起名为 Fescar,后改名为 Seata,在它开源之初,我就知道它肯定要火,因为这是一个解决痛点的开源项目,Seata 一开始就是冲着对业务无侵入与高性能方向走,这正是我们对解决分布式事务问题迫切的需求。因为待过的几家公司,用的都是微服务架构,但是在解决分布式事务的问题上都不太优雅,所以我也在一直关注 Seata 的发展,今天就简要说说它的一些设计上的原理,后续我将会对它的各个模块进行深入源码分析,感兴趣的可以持续关注我的公众号或者博客,不要跟丢。

分布式事务解决的方案有哪些?

目前分布式事务解决的方案主要有对业务无入侵和有入侵的方案,无入侵方案主要有基于数据库 XA 协议的两段式提交(2PC)方案,它的优点是对业务代码无入侵,但是它的缺点也是很明显:必须要求数据库对 XA 协议的支持,且由于 XA 协议自身的特点,它会造成事务资源长时间得不到释放,锁定周期长,而且在应用层上面无法干预,因此它性能很差,它的存在相当于七伤拳那样“伤人七分,损己三分”,因此在互联网项目中并不是很流行这种解决方案。

为了这个弥补这种方案带来性能低的问题,大佬们又想出了很多种方案来解决,但这无一例外都需要通过在应用层做手脚,即入侵业务的方式,比如很出名的 TCC 方案,基于 TCC 也有很多成熟的框架,如 ByteTCC、tcc-transaction 等。以及基于可靠消息的最终一致性来实现,如 RocketMQ 的事务消息。

入侵代码的方案是基于现有情形“迫不得已”才推出的解决方案,实际上它们实现起来非常不优雅,一个事务的调用通常伴随而来的是对该事务接口增加一系列的反向操作,比如 TCC 三段式提交,提交逻辑必然伴随着回滚的逻辑,这样的代码会使得项目非常臃肿,维护成本高。

Seata 各模块之间的关系

针对上面所说的分布式事务解决方案的痛点,那很显然,我们理想的分布式事务解决方案肯定是性能要好而且要对业务无入侵,业务层上无需关心分布式事务机制的约束,Seata 正是往这个方向发展的,因此它非常值得期待,它将给我们的微服务架构带来质的提升。

那 Seata 是怎么做到的呢?下面说说它的各个模块之间的关系。

Seata 的设计思路是将一个分布式事务可以理解成一个全局事务,下面挂了若干个分支事务,而一个分支事务是一个满足 ACID 的本地事务,因此我们可以操作分布式事务像操作本地事务一样。

Seata 内部定义了 3个模块来处理全局事务和分支事务的关系和处理过程,这三个组件分别是:

  • Transaction Coordinator (TC):事务协调器,维护全局事务的运行状态,负责协调并驱动全局事务的提交或回滚。
  • Transaction Manager (TM):控制全局事务的边界,负责开启一个全局事务,并最终发起全局提交或全局回滚的决议。
  • Resource Manager (RM):控制分支事务,负责分支注册、状态汇报,并接收事务协调器的指令,驱动分支(本地)事务的提交和回滚。

开发者说 | 分布式事务中间件 Seata 的设计原理

简要说说整个全局事务的执行步骤:

  • TM 向 TC 申请开启一个全局事务,TC 创建全局事务后返回全局唯一的 XID,XID 会在全局事务的上下文中传播;
  • RM 向 TC 注册分支事务,该分支事务归属于拥有相同 XID 的全局事务;
  • TM 向 TC 发起全局提交或回滚;
  • TC 调度 XID 下的分支事务完成提交或者回滚。

与 XA 方案有什么不同?

Seata 的事务提交方式跟 XA 协议的两段式提交在总体上来说基本是一致的,那它们之间有什么不同呢?

我们都知道 XA 协议它依赖的是数据库层面来保障事务的一致性,也即是说 XA 的各个分支事务是在数据库层面上驱动的,由于 XA 的各个分支事务需要有 XA 的驱动程序,一方面会导致数据库与 XA 驱动耦合,另一方面它会导致各个分支的事务资源锁定周期长,这也是它没有在互联网公司流行的重要因素。

基于 XA 协议以上的问题,Seata 另辟蹊径,既然在依赖数据库层会导致这么多问题,那我就从应用层做手脚,这还得从 Seata 的 RM 模块说起,前面也说过 RM 的主要作用了,其实 RM 在内部做了对数据库操作的代理层,如下:

开发者说 | 分布式事务中间件 Seata 的设计原理

Seata 在数据源做了一层代理层,所以我们使用 Seata 时,我们使用的数据源实际上用的是 Seata 自带的数据源代理 DataSourceProxy,Seata 在这层代理中加入了很多逻辑,主要是解析 SQL,把业务数据在更新前后的数据镜像组织成回滚日志,并将 undo log 日志插入 undo_log 表中,保证每条更新数据的业务 sql 都有对应的回滚日志存在。

这样做的好处就是,本地事务执行完可以立即释放本地事务锁定的资源,然后向 TC 上报分支状态。当 TM 决议全局提交时,就不需要同步协调处理了,TC 会异步调度各个 RM 分支事务删除对应的 undo log 日志即可,这个步骤非常快速地可以完成;当 TM 决议全局回滚时,RM 收到 TC 发送的回滚请求,RM 通过 XID 找到对应的 undo log 回滚日志,然后执行回滚日志完成回滚操作。

开发者说 | 分布式事务中间件 Seata 的设计原理

如上图所示,XA 方案的 RM 是放在数据库层的,它依赖了数据库的 XA 驱动程序。

开发者说 | 分布式事务中间件 Seata 的设计原理

如上图所示,Seata 的 RM 实际上是已中间件的形式放在应用层,不用依赖数据库对协议的支持,完全剥离了分布式事务方案对数据库在协议支持上的要求。

分支事务如何提交和回滚?

下面详细说说分支事务是如何提交和回滚的:

第一阶段:

分支事务利用 RM 模块中对 JDBC 数据源代理,加入了若干流程,对业务 SQL 进行解释,把业务数据在更新前后的数据镜像组织成回滚日志,并生成 undo log 日志,对全局事务锁的检查以及分支事务的注册等,利用本地事务 ACID 特性,将业务 SQL 和 undo log 写入同一个事物中一同提交到数据库中,保证业务 SQL 必定存在相应的回滚日志,最后对分支事务状态向 TC 进行上报。

开发者说 | 分布式事务中间件 Seata 的设计原理

第二阶段:

TM决议全局提交:

当 TM 决议提交时,就不需要同步协调处理了,TC 会异步调度各个 RM 分支事务删除对应的 undo log 日志即可,这个步骤非常快速地可以完成。这个机制对于性能提升非常关键,我们知道正常的业务运行过程中,事务执行的成功率是非常高的,因此可以直接在本地事务中提交,这步对于提升性能非常显著。

开发者说 | 分布式事务中间件 Seata 的设计原理

TM决议全局回滚:
当 TM 决议回滚时,RM 收到 TC 发送的回滚请求,RM 通过 XID 找到对应的 undo log 回滚日志,然后利用本地事务 ACID 特性,执行回滚日志完成回滚操作并删除 undo log 日志,最后向 TC 进行回滚结果上报。

开发者说 | 分布式事务中间件 Seata 的设计原理

业务对以上所有的流程都无感知,业务完全不关心全局事务的具体提交和回滚,而且最重要的一点是 Seata 将两段式提交的同步协调分解到各个分支事务中了,分支事务与普通的本地事务无任何差异,这意味着我们使用 Seata 后,分布式事务就像使用本地事务一样,完全将数据库层的事务协调机制交给了中间件层 Seata 去做了,这样虽然事务协调搬到应用层了,但是依然可以做到对业务的零侵入,从而剥离了分布式事务方案对数据库在协议支持上的要求,且 Seata 在分支事务完成之后直接释放资源,极大减少了分支事务对资源的锁定时间,完美避免了 XA 协议需要同步协调导致资源锁定时间过长的问题。

其它方案的补充

上面说的其实是 Seata 的默认模式,也叫 AT 模式,它是类似于 XA 方案的两段式提交方案,并且是对业务无侵入,但是这种机制依然是需要依赖数据库本地事务的 ACID 特性,有没有发现,我在上面的图中都强调了必须是支持 ACID 特性的关系型数据库,那么问题就来了,非关系型或者不支持 ACID 的数据库就无法使用 Seata 了,别慌,Seata 现阶段为我们准备了另外一种模式,叫 MT 模式,它是一种对业务有入侵的方案,提交回滚等操作需要我们自行定义,业务逻辑需要被分解为 Prepare/Commit/Rollback 3 部分,形成一个 MT 分支,加入全局事务,它存在的意义是为 Seata 触达更多的场景。

开发者说 | 分布式事务中间件 Seata 的设计原理

只不过,它不是 Seata “主打”的模式,它的存在仅仅作为补充的方案,从以上官方的发展远景就可以看出来,Seata 的目标是始终是对业务无入侵的方案。



本文作者:中间件小哥

原文链接

本文为云栖社区原创内容,未经允许不得转载。

点赞
收藏
评论区
推荐文章
美凌格栋栋酱 美凌格栋栋酱
9个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
Wesley13 Wesley13
4年前
SOA面向服务的分布式架构详解
导语:SOA作为一种面向服务的架构,是一种软件架构设计的模型和方法论。从业务角度来看,一切以最大化“服务”的价值为出发点,SOA利用企业现有的各种软件体系,重新整合并构建起一套新的软件架构。这套软件架构能够随着业务的变化,随时灵活地结合现有服务,组成新软件,共同服务于整个企业的业务体系。简单的理解,我们可以把SOA看作是模块化的组件,每个模块都可以实现独立功
如何在微服务下保证事务的一致性
随着业务的快速发展、业务复杂度越来越高,传统单体应用逐渐暴露出了一些问题,例如开发效率低、可维护性差、架构扩展性差、部署不灵活、健壮性差等等。而微服务架构是将单个服务拆分成一系列小服务,且这些小服务都拥有独立的进程,彼此独立,很好地解决了传统单体应用的上述问题,但是在微服务架构下如何保证事务的一致性呢?
Wesley13 Wesley13
4年前
SOA与微服务
微服务并非它的体积足够小,而是它的责任足够单一,很多人误解了「微」的真实含义,认为服务拆分得足够小就是微服务了,其实并非这样。此外,「微」还有“微不足道”的意思,也就是说,某个服务出现故障,它不会影响整个系统。微服务并非细粒度服务的组合,也就是说,粒度要细到什么程度,这取决于对业务功能的把控能力。此外,微服务是一种架构思想,包括看
Stella981 Stella981
4年前
Service Mesh 最火项目: Istio 架构解析
Istio是一个开源的服务网格,可为分布式微服务架构提供所需的基础运行和管理要素。随着各组织越来越多地采用云平台,开发者必须使用微服务设计架构以实现可移植性,而运维人员必须管理包含混合云部署和多云部署的大型分布式应用。Istio采用一种一致的方式来保护、连接和监控微服务,降低了管理微服务部署的复杂性。从架构设计上来看,Istio服务网格在逻辑上分为
Stella981 Stella981
4年前
Spring Cloud 与 Dubbo 对比整理(2)
一般初期公司需要面对非常复杂的业务场景,而且随着业务的发展,变化的可能性非常高。所以在微服务架构设计之初,我们就期望我们的微服务体系能:不绑定到特定的框架、语言服务最好是Restful风格足够简单,容易落地,将来能扩展和Docker相容性好目前常见的微服务相关框架:Dubbo、DubboXS
Wesley13 Wesley13
4年前
DDD分层架构最佳实践
还在单体应用的时候就是分层架构一说,我们用得最多的就是三层架构。而现在已经是微服务时代,在微服务架构模型比较常用的有几个,例如:整洁架构,CQRS(命令查询分离)以及六边形架构。每种架构模型都有自己的应用场景,但其核心都是“高内聚低耦合”原则。而运用领域驱动设计(DDD)理念以应对日常加速的业务变化对架构的影响,架构的边界越来越清晰,各司其职,这也符
Stella981 Stella981
4年前
Noark入门之线程模型
0x00单线程多进程单线程与单进程多线程的目的都是想尽可能的利用CPU,减少CPU的空闲时间,特别是多核环境,今天咱不做深度解读,跳过...0x01线程池锁最早的一部分游戏服务器是采用线程池的方式来处理玩家的业务请求,以达最大限度的利用多核优势来提高处理业务能力。但线程池同时也带来了并发问题,为了解决同一玩家多个业务请求不被
Stella981 Stella981
4年前
Dubbo与Zookeeper、SpringMVC整合和使用(负载均衡、容错)
!(https://oscimg.oschina.net/oscnet/771a0d0e23e340899ccabdd804af8569.gif)互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构势在必行,Dubbo是一个分布式服务框架,在这种情况下诞生的。现在核心业务抽取出来,作为独立的服务,
Stella981 Stella981
4年前
Devops下的分布式监控方案
_1基础监控的设计需求__现在devops,云计算,微服务,容器,大数据等理念正在逐步落地和大力发展,企业的服务器越来越多,架构越来越复杂,相应的应用运行基础环境越来越多样化,服务越来越微化,带来的监控压力也越来越大;_!(http://www.lvkaineng.com/wpcontent/u