分布式事务 Seata Saga 模式首秀以及三种模式详解 | Meetup#3 回顾

做者:屹远(陈龙),蚂蚁金服分布式事务核心研发 。
本文根据 8月11日 SOFA Meetup#3 广州站 《分布式事务 Seata 及其三种模式详解》主题分享整理,着重分享分布式事务产生的背景、理论基础,以及 Seata 分布式事务的原理以及三种模式(AT、TCC、Saga)的分布式事务实现。git

本次分享的视频回顾以及 PPT 查看地址:https://tech.antfin.com/community/activities/779/reviewgithub

分布式事务 Seata 三种模式详解

1、分布式事务产生的背景

1.1 分布式架构演进之 - 数据库的水平拆分

蚂蚁金服的业务数据库起初是单库单表,但随着业务数据规模的快速发展,数据量愈来愈大,单库单表逐渐成为瓶颈。因此咱们对数据库进行了水平拆分,将原单库单表拆分红数据库分片。数据库

以下图所示,分库分表以后,原来在一个数据库上就能完成的写操做,可能就会跨多个数据库,这就产生了跨数据库事务问题。json

数据库的水平拆分

1.2 分布式架构演进之 - 业务服务化拆分

在业务发展初期,“一块大饼”的单业务系统架构,能知足基本的业务需求。可是随着业务的快速发展,系统的访问量和业务复杂程度都在快速增加,单系统架构逐渐成为业务发展瓶颈,解决业务系统的高耦合、可伸缩问题的需求愈来愈强烈。网络

以下图所示,蚂蚁金服按照面向服务架构(SOA)的设计原则,将单业务系统拆分红多个业务系统,下降了各系统之间的耦合度,使不一样的业务系统专一于自身业务,更有利于业务的发展和系统容量的伸缩。架构

业务服务化拆分

业务系统按照服务拆分以后,一个完整的业务每每须要调用多个服务,如何保证多个服务间的数据一致性成为一个难题。并发

2、分布式事务理论基础

2.1 两阶段提交协议

两阶段提交协议

两阶段提交协议:事务管理器分两个阶段来协调资源管理器,第一阶段准备资源,也就是预留事务所需的资源,若是每一个资源管理器都资源预留成功,则进行第二阶段资源提交,不然协调资源管理器回滚资源。框架

2.2 TCC

TCC

TCC(Try-Confirm-Cancel) 其实是服务化的两阶段提交协议,业务开发者须要实现这三个服务接口,第一阶段服务由业务代码编排来调用 Try 接口进行资源预留,全部参与者的 Try 接口都成功了,事务管理器会提交事务,并调用每一个参与者的 Confirm 接口真正提交业务操做,不然调用每一个参与者的 Cancel 接口回滚事务。异步

2.3 Saga

Saga

Saga 是一种补偿协议,在 Saga 模式下,分布式事务内有多个参与者,每个参与者都是一个冲正补偿服务,须要用户根据业务场景实现其正向操做和逆向回滚操做。分布式

分布式事务执行过程当中,依次执行各参与者的正向操做,若是全部正向操做均执行成功,那么分布式事务提交。若是任何一个正向操做执行失败,那么分布式事务会退回去执行前面各参与者的逆向回滚操做,回滚已提交的参与者,使分布式事务回到初始状态。

Saga 理论出自 Hector & Kenneth 1987发表的论文 Sagas。

Saga 正向服务与补偿服务也须要业务开发者实现。

3、Seata 及其三种模式详解

3.1 分布式事务 Seata 介绍

Seata(Simple Extensible Autonomous Transaction Architecture,简单可扩展自治事务框架)是 2019 年 1 月份蚂蚁金服和阿里巴巴共同开源的分布式事务解决方案。Seata 开源半年左右,目前已经有超过 1.1 万 star,社区很是活跃。咱们热忱欢迎你们参与到 Seata 社区建设中,一同将 Seata 打形成开源分布式事务标杆产品。

Seata:https://github.com/seata/seata

Seata

3.2 分布式事务 Seata 产品模块

以下图所示,Seata 中有三大模块,分别是 TM、RM 和 TC。 其中 TM 和 RM 是做为 Seata 的客户端与业务系统集成在一块儿,TC 做为 Seata 的服务端独立部署。

Seata 三大模块

在 Seata 中,分布式事务的执行流程:

  • TM 开启分布式事务(TM 向 TC 注册全局事务记录);
  • 按业务场景,编排数据库、服务等事务内资源(RM 向 TC 汇报资源准备状态 );
  • TM 结束分布式事务,事务一阶段结束(TM 通知 TC 提交/回滚分布式事务);
  • TC 汇总事务信息,决定分布式事务是提交仍是回滚;
  • TC 通知全部 RM 提交/回滚 资源,事务二阶段结束;

3.3 分布式事务 Seata 解决方案

Seata 会有 4 种分布式事务解决方案,分别是 AT 模式、TCC 模式、Saga 模式和 XA 模式。

Seata 的 4 种分布式事务解决方案

3.3.1 AT 模式

今年 1 月份,Seata 开源了 AT 模式。AT 模式是一种无侵入的分布式事务解决方案。在 AT 模式下,用户只需关注本身的“业务 SQL”,用户的 “业务 SQL” 做为一阶段,Seata 框架会自动生成事务的二阶段提交和回滚操做。

AT 模式

AT 模式如何作到对业务的无侵入 :
  • 一阶段:

在一阶段,Seata 会拦截“业务 SQL”,首先解析 SQL 语义,找到“业务 SQL”要更新的业务数据,在业务数据被更新前,将其保存成“before image”,而后执行“业务 SQL”更新业务数据,在业务数据更新以后,再将其保存成“after image”,最后生成行锁。以上操做所有在一个数据库事务内完成,这样保证了一阶段操做的原子性。

AT 模式一阶段

  • 二阶段提交:

二阶段若是是提交的话,由于“业务 SQL”在一阶段已经提交至数据库, 因此 Seata 框架只需将一阶段保存的快照数据和行锁删掉,完成数据清理便可。

AT 模式二阶段提交

  • 二阶段回滚:

二阶段若是是回滚的话,Seata 就须要回滚一阶段已经执行的“业务 SQL”,还原业务数据。回滚方式即是用“before image”还原业务数据;但在还原前要首先要校验脏写,对比“数据库当前业务数据”和 “after image”,若是两份数据彻底一致就说明没有脏写,能够还原业务数据,若是不一致就说明有脏写,出现脏写就须要转人工处理。

AT 模式二阶段回滚

AT 模式的一阶段、二阶段提交和回滚均由 Seata 框架自动生成,用户只需编写“业务 SQL”,便能轻松接入分布式事务,AT 模式是一种对业务无任何侵入的分布式事务解决方案。

3.3.2 TCC 模式

2019 年 3 月份,Seata 开源了 TCC 模式,该模式由蚂蚁金服贡献。TCC 模式须要用户根据本身的业务场景实现 Try、Confirm 和 Cancel 三个操做;事务发起方在一阶段执行 Try 方式,在二阶段提交执行 Confirm 方法,二阶段回滚执行 Cancel 方法。

TCC 模式

TCC 三个方法描述:

  • Try:资源的检测和预留;
  • Confirm:执行的业务操做提交;要求 Try 成功 Confirm 必定要能成功;
  • Cancel:预留资源释放;

蚂蚁金服在 TCC 的实践经验

蚂蚁金服在 TCC 的实践经验

1 TCC 设计 - 业务模型分 2 阶段设计:

用户接入 TCC ,最重要的是考虑如何将本身的业务模型拆成两阶段来实现。

以“扣钱”场景为例,在接入 TCC 前,对 A 帐户的扣钱,只需一条更新帐户余额的 SQL 便能完成;可是在接入 TCC 以后,用户就须要考虑如何将原来一步就能完成的扣钱操做,拆成两阶段,实现成三个方法,而且保证一阶段 Try  成功的话 二阶段 Confirm 必定能成功。

TCC 设计 - 业务模型分 2 阶段设计

如上图所示,Try 方法做为一阶段准备方法,须要作资源的检查和预留。在扣钱场景下,Try 要作的事情是就是检查帐户余额是否充足,预留转帐资金,预留的方式就是冻结 A 帐户的 转帐资金。Try 方法执行以后,帐号 A 余额虽然仍是 100,可是其中 30 元已经被冻结了,不能被其余事务使用。

二阶段 Confirm 方法执行真正的扣钱操做。Confirm 会使用 Try 阶段冻结的资金,执行帐号扣款。Confirm 方法执行以后,帐号 A 在一阶段中冻结的 30 元已经被扣除,帐号 A 余额变成 70 元 。

若是二阶段是回滚的话,就须要在 Cancel 方法内释放一阶段 Try 冻结的 30 元,使帐号 A 的回到初始状态,100 元所有可用。

用户接入 TCC 模式,最重要的事情就是考虑如何将业务模型拆成 2 阶段,实现成 TCC 的 3 个方法,而且保证 Try 成功 Confirm 必定能成功。相对于 AT 模式,TCC 模式对业务代码有必定的侵入性,可是 TCC 模式无 AT 模式的全局行锁,TCC 性能会比 AT 模式高不少。

2 TCC 设计 - 容许空回滚:

TCC 设计 - 容许空回滚

Cancel 接口设计时须要容许空回滚。在 Try 接口由于丢包时没有收到,事务管理器会触发回滚,这时会触发 Cancel 接口,这时 Cancel 执行时发现没有对应的事务 xid 或主键时,须要返回回滚成功。让事务服务管理器认为已回滚,不然会不断重试,而 Cancel 又没有对应的业务数据能够进行回滚。

3 TCC 设计 - 防悬挂控制:

TCC 设计 - 防悬挂控制

悬挂的意思是:Cancel 比 Try 接口先执行,出现的缘由是 Try 因为网络拥堵而超时,事务管理器生成回滚,触发 Cancel 接口,而最终又收到了 Try 接口调用,可是 Cancel 比 Try 先到。按照前面容许空回滚的逻辑,回滚会返回成功,事务管理器认为事务已回滚成功,则此时的 Try 接口不该该执行,不然会产生数据不一致,因此咱们在 Cancel 空回滚返回成功以前先记录该条事务 xid 或业务主键,标识这条记录已经回滚过,Try 接口先检查这条事务xid或业务主键若是已经标记为回滚成功过,则不执行 Try 的业务操做。

4 TCC 设计 - 幂等控制:

CC 设计 - 幂等控制

幂等性的意思是:对同一个系统,使用一样的条件,一次请求和重复的屡次请求对系统资源的影响是一致的。由于网络抖动或拥堵可能会超时,事务管理器会对资源进行重试操做,因此极可能一个业务操做会被重复调用,为了避免由于重复调用而屡次占用资源,须要对服务设计时进行幂等控制,一般咱们能够用事务 xid 或业务主键判重来控制。

3.3.3 Saga 模式

Saga 模式是 Seata 即将开源的长事务解决方案,将由蚂蚁金服主要贡献。在 Saga 模式下,分布式事务内有多个参与者,每个参与者都是一个冲正补偿服务,须要用户根据业务场景实现其正向操做和逆向回滚操做。

分布式事务执行过程当中,依次执行各参与者的正向操做,若是全部正向操做均执行成功,那么分布式事务提交。若是任何一个正向操做执行失败,那么分布式事务会去退回去执行前面各参与者的逆向回滚操做,回滚已提交的参与者,使分布式事务回到初始状态。

Saga 模式

Saga 模式下分布式事务一般是由事件驱动的,各个参与者之间是异步执行的,Saga 模式是一种长事务解决方案。

1 Saga 模式使用场景

Saga 模式使用场景

Saga 模式适用于业务流程长且须要保证事务最终一致性的业务系统,Saga 模式一阶段就会提交本地事务,无锁、长流程状况下能够保证性能。

事务参与者多是其它公司的服务或者是遗留系统的服务,没法进行改造和提供 TCC 要求的接口,可使用 Saga 模式。

Saga模式的优点是:

  • 一阶段提交本地数据库事务,无锁,高性能;
  • 参与者能够采用事务驱动异步执行,高吞吐;
  • 补偿服务即正向服务的“反向”,易于理解,易于实现;

缺点:Saga 模式因为一阶段已经提交本地数据库事务,且没有进行“预留”动做,因此不能保证隔离性。后续会讲到对于缺少隔离性的应对措施。

2 基于状态机引擎的 Saga 实现

基于状态机引擎的 Saga 实现

目前 Saga 的实现通常也两种,一种是经过事件驱动架构实现,一种是基于注解加拦截器拦截业务的正向服务实现。Seata 目前是采用事件驱动的机制来实现的,Seata 实现了一个状态机,能够编排服务的调用流程及正向服务的补偿服务,生成一个 json 文件定义的状态图,状态机引擎驱动到这个图的运行,当发生异常的时候状态机触发回滚,逐个执行补偿服务。固然在什么状况下触发回滚用户是能够自定义决定的。该状态机能够实现服务编排的需求,它支持单项选择、并发、异步、子状态机调用、参数转换、参数映射、服务执行状态判断、异常捕获等功能。

3 状态机引擎原理

状态机引擎原理

该状态机引擎的基本原理是,它基于事件驱动架构,每一个步骤都是异步执行的,步骤与步骤之间经过事件队列流转,
极大的提升系统吞吐量。每一个步骤执行时会记录事务日志,用于出现异常时回滚时使用,事务日志会记录在与业务表所在的数据库内,提升性能。

4 状态机引擎设计

状态机引擎设计

该状态机引擎分红了三层架构的设计,最底层是“事件驱动”层,实现了 EventBus 和消费事件的线程池,是一个 Pub-Sub 的架构。第二层是“流程控制器”层,它实现了一个极简的流程引擎框架,它驱动一个“空”的流程执行,“空”的意思是指它不关心流程节点作什么事情,它只执行每一个节点的 process 方法,而后执行 route 方法流转到下一个节点。这是一个通用框架,基于这两层,开发者能够实现任何流程引擎。最上层是“状态机引擎”层,它实现了每种状态节点的“行为”及“路由”逻辑代码,提供 API 和状态图仓库,同时还有一些其它组件,好比表达式语言、逻辑计算器、流水生成器、拦截器、配置管理、事务日志记录等。

5 Saga 服务设计经验

和TCC相似,Saga的正向服务与反向服务也需求遵循如下设计原则:

1)Saga 服务设计 - 容许空补偿

Saga 服务设计 - 容许空补偿

2)Saga 服务设计 - 防悬挂控制

Saga 服务设计 - 防悬挂控制

3)Saga 服务设计 - 幂等控制

Saga 服务设计 - 幂等控制

4)Saga 设计 - 自定义事务恢复策略

Saga 设计 - 自定义事务恢复策略

前面讲到 Saga 模式不保证事务的隔离性,在极端状况下可能出现脏写。好比在分布式事务未提交的状况下,前一个服务的数据被修改了,然后面的服务发生了异常须要进行回滚,可能因为前面服务的数据被修改后没法进行补偿操做。这时的一种处理办法能够是“重试”继续往前完成这个分布式事务。因为整个业务流程是由状态机编排的,即便是过后恢复也能够继续往前重试。因此用户能够根据业务特色配置该流程的事务处理策略是优先“回滚”仍是“重试”,当事务超时的时候,Server 端会根据这个策略不断进行重试。

因为 Saga 不保证隔离性,因此咱们在业务设计的时候须要作到“宁肯长款,不可短款”的原则,长款是指在出现差错的时候站在我方的角度钱多了的状况,钱少了则是短款,由于若是长款能够给客户退款,而短款则可能钱追不回来了,也就是说在业务设计的时候,必定是先扣客户账再入账,若是由于隔离性问题形成覆盖更新,也不会出现钱少了的状况。

6 基于注解和拦截器的 Saga 实现

基于注解和拦截器的 Saga 实现

还有一种 Saga 的实现是基于注解+拦截器的实现,Seata 目前没有实现,能够看上面的伪代码来理解一下,one 方法上定义了 @SagaCompensable 的注解,用于定义 one 方法的补偿方法是 compensateOne 方法。而后在业务流程代码 processA 方法上定义 @SagaTransactional 注解,启动 Saga 分布式事务,经过拦截器拦截每一个正向方法当出现异常的时候触发回滚操做,调用正向方法的补偿方法。

7 两种 Saga 实现优劣对比

两种 Saga 的实现各有又缺点,下面表格是一个对比:

两种 Saga 实现优劣对比

状态机引擎的最大优点是能够经过事件驱动的方法异步执行提升系统吞吐,能够实现服务编排需求,在 Saga 模式缺少隔离性的状况下,能够多一种“向前重试”的事情恢复策略。注解加拦截器的的最大优点是,开发简单、学习成本低。

总结

本文先回顾了分布式事务产生的背景及理论基础,而后重点讲解了 Seata 分布式事务的原理以及三种模式(AT、TCC、Saga)的分布式事务实现。

Seata 的定位是分布式事全场景解决方案,将来还会有 XA 模式的分布式事务实现,每种模式都有它的适用场景,AT 模式是无侵入的分布式事务解决方案,适用于不但愿对业务进行改造的场景,几乎0学习成本。TCC 模式是高性能分布式事务解决方案,适用于核心系统等对性能有很高要求的场景。Saga 模式是长事务解决方案,适用于业务流程长且须要保证事务最终一致性的业务系统,Saga 模式一阶段就会提交本地事务,无锁,长流程状况下能够保证性能,多用于渠道层、集成层业务系统。事务参与者多是其它公司的服务或者是遗留系统的服务,没法进行改造和提供 TCC 要求的接口,也可使用 Saga 模式。

相关文章
相关标签/搜索