0009 一种分布式事务的落地实践方案

Posted on Tue, May 10, 2022 分布式事务 saga 解决方案
🤺

本文是基于SAGA分布式事务论文的实现。

1、缩略语和关键术语定义

MQ(Message Queue,消息队列):在消息的传输过程中保存消息的容器。

ACID(Atomicity原子性、Consistency一致性、Isolation隔离性、Durability持久性的缩写):指数据库管理系统在写入或更新资料的过程中,为保证事务是正确可靠的,所必须具备的四个特性。

BASE(Basically Available基本可用、Soft State软状态、Eventually Consistent最终一致性的缩写):是指分布式应用无法做到强一致,但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性。

2、背景技术方案

2.1 详细介绍背景技术方案

事务是指由一个或多个资源管理操作构成的操作序列,它具有原子性、一致性、隔离性和持久性,即ACID特征。分布式事务是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上。对一致性时效要求不是特别高的场景,通常使用柔性事务(满足BASE特征)替代严苛的刚性事务(满足ACID特征),即不追求数据的强一致性保证其最终一致性。随着当前的数据库几乎都已支持事务,这使得单个服务节点具备事务能力(即本地事务)已逐渐成为标配。目前许多分布式柔性事务都是基于本地事务,主要代表为消息事务方式。

消息事务方式是将业务数据和本地事务状态共同存放到本地数据库,同时提供本地事务状态的查询接口。请求方先预提交请求,然后执行本地事务,事务成功则再次提交确认,MQ将消息传递给订阅方,若事务执行失败MQ将丢弃预提交请求。下图所示为消息事务的流程,具体步骤如下:

S101:消息请求方先将请求提交到MQ,但MQ暂不能投递该消息,需要收到发送方对该消息的二次确认才会投递。这种被标记成“暂不能投递”状态的消息,又叫半消息,通常采用RocketMQ来处理这种特殊消息;

S102:MQ向消息发送方确认消息是否接收成功;

S103:消息请求方收到S102的确认后,执行本地事务,更新本地事务状态为成功或失败;

S104:若本地事务执行成功,则再次向MQ确认提交,反之则告知MQ已提交的消息需要回滚;

S105:若MQ未收到S104步骤的消息,将回查消息请求方的本地事务状态查询接口,判断事务执行成功还是失败;

S106:消息请求方查询本地事务执行状态,根据事务状态选择提交确认还是回滚;

S107:将提交确认或回滚消息发送给MQ;

S108:若是提交确认,MQ将消息投递给消息订阅方;

S109:若是回滚消息,MQ将S101预提交的消息丢弃。

从以上步骤可知,消息事务其实是通过本地事务来保证分布式事务的,且依赖具备半消息处理能力的RocketMQ。从示意图可以看到,本地事务数据和业务数据是耦合在一起,且由服务节点各自管理,一旦本地事务数据丢失或不可用,整个事务将无法回滚。

2.2 本技术要解决的技术问题

1)、该分布式事务方案依赖特殊的MQ,不具备很好的通用性和移植性。

2)、事务数据与业务数据高耦合,致使事务的独立自治变得困难。

3)、事务一旦失败就立即回滚,缺乏合理的重试机制。

3、技术的创新点

1)、设计了一种分布式事务协调管理器,不依赖MQ特性,对事务数据独立自治,通过插拔式的接口实现通用性。

2)、设计了一种基于决策矩阵的事务调度算法。

3)、通过合理重试失败事务,提升分布式事务的执行成功率。

4、本技术方案及有益效果

4.1 分布式事务协调管理装置

本技术设计了一种独立于服务节点、应用自治的插拔式分布式协调管理装置。对任意具有N个服务节点(编号从1到N)的分布式系统,每个服务节点都对应着一个本地事务和一个事务补偿操作,所有本地事务构成完整的分布式事务,即:

T=T1(C1)+T2(C2)+...+Ti(Ci)+...TN(CN)T=T1(C1)+T2(C2)+...+Ti(Ci)+...TN(CN)

以上公示中的为全局分布式事务,表示第i个服务节点的本地事务及其对应的补偿操作。补偿操作本质就是事务回滚,对于具有本地事务的节点来说是天然具备的,因为在数据库层面事务与回滚几乎都能同时保证。即将节点的回滚操作封装到接口中,可由外部调用触发。

图1 可回滚和重试的分布式事务协调管理器

图1所示为本技术设计的分布式协调管理器(以下简称管理器),当用户请求进入服务节点后,会经过事务拦截器(如图2所示),拦截器将事务上报给管理器,节点执行完本地事务后,拦截器再次上报执行状态,整个过程对业务处理是无感的。当分布式中的某个节点事务执行失败,经管理器的决策算法判断是下发回滚还是重试指令,若是回滚会不断触发调用链上的节点进行回滚。其中,分布式节点的事务状态都由管理器存储、加工和计算,实现事务数据与业务数据的松散耦合。

图2为事务拦截器的示意图,根据事务处理的事前、事中和事后原则进行设计,每个步骤的含义为:

S201:事前步骤,服务节点接受到服务请求,将事务初始化信息上报管理器;

S202:事中步骤,该步骤为业务处理,执行对应的本地事务;

S203:事后步骤,本地事务执行完成后,拦截器将本地事务状态上报管理器。

图2 事务拦截器装置结构图

4.2 分布式事务协调管理过程

在上一节的装置结构基础上,本节主要介绍管理器的执行过程。图3所示为本技术的分布式事务协调管理过程:

图3 分布式事务协调管理执行过程

S301:服务节点在接入统一的分布式事务管理之前,需要向管理器注册事务节点,提供本地事务的补偿方法接口;

S302:管理器为该节点创建一个记录其事务状态的事务表,并生成全局唯一的事务ID值,该ID值与事务表名一一对应,目的是避免多节点共用同一张事务表出现性能问题。同时,管理器还会为补偿方法生成对应的补偿ID;

S303:管理器创建事务表成功后,将对应的事务ID值分配给服务节点,至此完成事务管理服务的注册过程;

S304:当服务请求进入后,拦截器上报事务的初始状态,上传信息包括:当前服务节点ID、调用方服务节点ID、补偿接口ID;

S305:管理器收到上报的事务后,根据唯一事务ID寻址对应的事务表,在事务表中插入一条初始化的事务数据,同时根据服务节点ID和调用方服务节点ID关系,利用Zipkin算法[1]生成分布式调用链路;

S306:服务节点的本地事务执行步骤;

S307:将本地事务执行的状态(成功或失败)上报管理器;

S308:通过管理器的调度算法决策,决定事务前向重试或后向补偿,同时更新对应事务流水的状态;

S309:若本地事务是执行成功的,则流程将顺利执行到下一个服务节点;

S310:若本地事务执行状态为失败,经决策需要后向补偿,根据管理器生成的分布式调用链,从该节点开始,执行后向事务补偿,若需要重试,则执行事务重试;

S311:执行本地事务补偿,若补偿失败会不断重试直到成功,该步骤由人工干预兜底保证。如果是事务重试,则会重试一定次数再决定是否启动补偿。

S312:本地事务补偿成功后将结果上报管理器;

S313:管理器根据调用链,调度上一个服务节点,传送事务回滚的指令,重复S310-S312步骤直到所有补偿都完成则流程结束;

S314:本地事务重试成功,上报事务结果;

S315:流程顺利执行到下一个服务节点。

以上步骤可以总结为,服务节点要接入管理器,首先需要注册节点事务ID,有了这个ID则表示管理器将该节点的事务纳入统一管理。服务节点侧,当请求进入服务节点后,通过事务拦截装置先上报事务,节点执行完事务后再将执行结果上报。管理器侧,通过上报的事务执行结果决策整个事务应该回滚还是有限次数的局部重试,最终实现分布式事务的统一管理。

本技术以APP用户注销场景为例,将操作案例贯穿整个流程进行说明。假设有这样一个APP,用户注销账户时,须将用户的积分兑换为话费充值到用户的手机号,完成积分兑换清零后,才注销用户状态及删除必要的用户信息。具体的分布式服务流程如图4所示:

图4 APP用户注销分布式调用流程

如图4中所示,注销流程共有5个分布式服务节点,用户发起的请求调用链按时间顺序为:[服务节点编号#5]-> [服务节点编号#4]-> [服务节点编号#3]-> [服务节点编号#2]-> [服务节点编号#1],而分布式服务执行是调用链的逆过程,即图中所示的顺序。下面是对应流程:

以上即为分布式事务协调管理的宏观过程,通过APP账户注销流程进行了演示说明。

4.3 分布式事务调度算法

本技术还设计了一种提高分布式事务成功率的事务调度算法,它内置于管理器装置,算法流程如图5所示。

图5 分布式事务调度算法流程图

假设分布式系统有N个服务节点,用i表示服务节点编号。流程开始后管理器监听节点i上报的事务,标记其事务状态为开始。节点i开始执行本地事务:

  1. 若决策结果为重试,则继续监听节点i并下发重试指令,该步骤对应图中的P1前向重试模块,该模块通过有限次数的合理重试,避免任务一失败就立即回滚,从而提升整个事务的成功率。
  2. 若决策结果为开启事务补偿,管理器记录节点i的状态为补偿开始,并下发补偿指令,节点i开始执行自己的补偿操作,若执行成功,更新节点i的事务状态为补偿成功,并调度下一个节点(i=i-1),直到所有节点都已完成补偿(i=0),整个分布式事务得以回滚。当然,补偿操作不一定一次就能执行成功,如果补偿失败,需要系统自动重试,如果重试超过配置的次数,需加入告警,由人工干预来保证节点的补偿执行成功。整个步骤对应图中的 P2后向补偿模块。

在以上调度算法中,决策矩阵是一个重要的创新点,本技术中提出的决策矩阵的设计如图6所示:

图6 决策矩阵

在决策矩阵中,第一列表示节点事务执行失败时的要素,第二、三列表示对应的选择:回滚或重试,第三、四列为重试时的可选方案和对应的可配置的最大重试次数(R1-R5)。重试最大次数R1-R5通常是根据实际生产运行监控数据总结出的经验值。决策矩阵中的各项内容为:

4.5 分布式事务装置

本技术的分布式事务装置的全局结构如图7所示,每个模块的功能为:

图7 分布式事务协调管理装置结构图

S401:监听单元,管理器负责监听服务节点上报事务的功能单元;

S402:通信接口单元,负责管理器与分布式集群之间的数据通信;

S403:事务上报单元,负责事务执行前后的拦截上报;

S404:用户接口,提供给服务节点的插拔式接口;

S405:事务指令执行单元,接受到管理器的事务执行指令(补偿或重试)后,由该单元负责执行;

S406:指令单元,管理器根据决策结果,生成调度指令,对应的指令集如图8所示。指令集共有六种指令,覆盖了回滚/重试当前节点、回滚/重试指定服务节点、停止回滚/重试一定的时间,指令的使用示例如表中最后一列所示。

图8 分布式事务调度指令集

以用户注销场景为例,当节点2充值话费服务失败,管理器发送的指令如下:

S407:事务注册器,管理器中负责服务节点事务管理注册的单元;

S408:处理器,指中央处理器CPU,负责管理器的计算任务;

S409:调度引擎,内置了本技术的调度算法,负责实时决策事务节点的调度任务;

S410:存储器,用于管理器中的数据存储,包括但不限于关系型数据库、非关系型数据库以及磁盘日志文件。

通过上述的管理过程、调度算法、管理装置、调度指令等一系列的技术设计,可以实现一套可方便移植、应用自治、成功率高的分布式柔性事务解决方案。

5、最优方案的有益效果

(1)通过本技术的分布式协调管理器,可以实现事务处理与业务处理的解耦,这种插拔式的管理器技术栈无关,具有通用性。

(2)通过本技术的分布式事务调度算法,能提升分布式事务的成功率。

(3)从服务节点单独管理事务数据,变为由管理器统一管理,避免了节点管理的事务数据不可用时全局事务的不可回滚。

6、本技术的替代方案

(1)本技术的分布式事务协调管理器,它是基于服务节点具有事务补偿能力,因此还有一种替代方案:

如果分布式系统中存在没有事务补偿能力的服务节点,为了提高分布式事务的执行成功率,在服务编排时,应尽量将具有本地事务补偿能力的节点安排在前面。对于没有本地事务补偿能力的节点,管理器可采用数据库快照方式(数据量最小化),记录节点数据库业务执行前的状态,生成回滚日志(即undo log)。当需要回滚没有事务补偿能力的节点时,由管理器代为补偿执行。

(2)对于本技术中使用的事务拦截器,可用过滤器或java 中的切面技术代替。

(3)对于决策矩阵中“调用链路节点均有补偿接口,且链路已过半”情况,对应的条件为i>N/2,其中i为节点编号(1到N),N为服务节点个数。除了i>N/2外,还可以根据后续节点的可用率均值进行调整,如果后续节点的可用率均值都比较高,条件可放宽到i>N/3、i>N/4等,依次类推。

7、参考文献

[1] jcchavezs. Openzipkin分布式链路生成算法[EB/OL]. https:// github.com/openzipkin/zipkin,2018-1-25/2022-02-02.

[2] https://microservices.io/patterns/data/saga.html

[3] http://servicecomb.incubator.apache.org/cn/docs/distributed-transactions-saga-implementation/

[4] https://www.cs.cornell.edu/andru/cs711/2002fa/reading/sagas.pdf