分布式事物:All or nothing

Distributed transaction: All or nothing

  PengTuo      May 17, 2020     1730 words      views

本文概览:

一、什么是分布式事务?

事务:是指包含一系列操作的、一个有边界的工作序列,有明确的开始和结束标志,且要么被完全执行,要么完全失败,即 all or nothing。

分布式事务:就是指在分布式系统中运行的事务,由多个本地事务组合而成。

事物具有 ACID 四个性质:

  • 原子性(Atomicity),即事务最终的状态只有两种,全部执行成功和全部不执行。
  • 一致性(Consistency),是指事务操作前和操作后,数据的完整性保持一致或满足完整性约束。
  • 隔离性(Isolation),是指当系统内有多个事务并发执行时,多个事务不会相互干扰,即一个事务内部的操作及使用的数据,对其他并发事务是隔离的。
  • 持久性(Durability),也被称为永久性,是指一个事务完成了,那么它对数据库所做的更新就被永久保存下来了。

1.1. 刚性事务与柔性事务

刚性事务,遵循 ACID 原则,具有强一致性。比如,数据库事务。而柔性事务,就是指根据不同的业务场景使用不同的方法实现最终一致性,可以容忍一定时间内的数据不一致,而柔性事务的最终一致性,遵循的是 BASE 理论。

BASE 理论包括:

  • 基本可用(Basically Available):基本可用:分布式系统出现故障的时候,允许损失一部分功能的可用性。
  • 柔性状态(Soft State):在柔性事务中,允许系统存在中间状态,且这个中间状态不会影响系统整体可用性。
  • 最终一致性(Eventual Consistency):事务在操作过程中可能会由于同步延迟等问题导致不一致,但最终状态下,数据都是一致的。

BASE 理论为了支持大型分布式系统,通过牺牲强一致性,保证最终一致性,来获得高可用性,是对 ACID 原则的弱化。

二、如何实现分布式事务?

实现分布式事务有以下 3 种基本方法:

  • 二阶段提交协议方法(The two-phase commit protocol,2PC);
  • 三阶段提交协议方法(3PC);
  • 基于消息的最终一致性方法;

其中前两种方法都是基于一种 XA 协议实现的,XA 协议是一个基于数据库的分布式事务协议,其分为两部分:事务管理器和本地资源管理器。事务管理器作为一个全局的协调者,负责对各个本地资源管理器统一号令提交或者回滚,然后本地资源管理器则是分布式事物的参与者,其往往由数据库实现。

2.1. 二阶段提交方法

基于 XA 协议的二阶段提交方法中,2PC 用于保证分布式系统中事务提交时的数据一致性,是 XA 协议在全局事务中用于协调多个资源的机制。

总体来说,为了保证多个资源的一致性,二阶段提交协议引入一个协调者(即事务管理器)来管理所有的节点,并确保这些节点正确提交操作结果,若提交失败则放弃事务,本质上就是对消息中间件的一种特殊利用,它是将本地事务和发消息放在了一个分布式事务里,保证要么本地操作成功成功并且对外发消息成功,要么两者都失败。

两阶段提交协议的执行过程,分为投票(voting)和提交(commit)两个阶段。

投票为第一阶段,协调者向所有的参与者(即本地资源管理器)发起 CanCommit 请求,当参与者接收到请求以后,执行请求中的事物操作,但是不进行提交操作,待参与者执行成功,则向协调者发送“Yes”消息,表示同意操作;若不成功,则发送“No”消息,表示终止操作。

提交为第二阶段,当协调者收到所有参与者返回的消息后,系统则是进入提交阶段,此时分为两种情况:

  • 若协调者收到所有的参与者发来的“Yes”消息,则表示所有的参与者事物操作成功可以提交,此时协调者向所有的参与者发送“DoCommit”消息,参与者会完成剩余的操作并释放资源,然后向协调者返回“HaveCommitted”消息;
  • 若协调者收到了参与者的“No”消息,则表示有参与者事物操作失败不能提交,此时协调者会向所有的参与者发送“DoAbort”消息,此时发送“Yes”的参与者则会根据之前执行操作的回滚日志进行回滚,然后所有参与者向协调者返回“HaveCommitted”消息;

当协调者接收到“HaveCommitted”消息后,整个分布式事物操作结束。由此过程也可以发现,二阶段提交方法是一种同步操作方法,要么全部成功,要么全部失败。

二阶段提交的算法思路可以概括为:协调者下发请求事务操作,参与者将操作结果通知协调者,协调者根据所有参与者的反馈结果决定各参与者是要提交操作还是撤销操作。

二阶段提交也具有几点明显缺点:

  • 单点故障问题:协调者是全局同步操作的关键点,执行过程中协调者宕机会导致参与者还都处于锁定资源的状态,无法完成事务commit操作;
  • 同步阻塞问题:因为二阶段是全局同步操作,所以如果有的参与者在执行过程中阻塞会导致其余参与者无法释放被占用资源;
  • 数据不一致问题:当协调者向参与者发送消息时,若此时网络抖动导致部分参与者未接收到消息,则会导致参与者之间数据不一致。

2.2. 三阶段提交方法

三阶段提交方法则是针对二阶段方法提出的一种改进,主要改进有两个方面:

  1. 首先是给协调者与所有参与者都加入了超时机制(2pc只是在协调者引入了超时),即当参与者超时未提交时,参与者不会继续等待协调者命令,而是根据当前状态选择下一步操作;
  2. 其次就是将二阶段的“提交阶段”一分为二,拆分为“PreCommit 阶段”与“DoCommit 阶段”,保证了在最后提交阶段之前,各参与者节点的状态都一致;

第一阶段 CanCommit 阶段:与 2PC 一样,协调者向所有的参与者发起 CanCommit 消息,询问参与者是否可以执行事务提交操作,如果全部参与者回复“Yes”则进入下一阶段,如果有的参与者回复“No”或者是协调者超时未接收到参与者消息,则协调者会发送 Abort 消息,中断此次事物操作。

第二阶段 PreCommit 阶段:协调者接收到所有的参与者发送的“Yes”消息后,向参与者发送 PreCommit 消息,进入预提交阶段,然后所有的参与者执行事务操作,并将 Undo 和 Redo 信息记录到事务日志中,并在执行完成后向参与者返回 ACK 响应,若协调者没有接收到参与者发送的 ACK 响应,会向所有参与者发送 Abort 请求命令,执行事务的中断。

第三阶段 DoCommit 阶段:如果协调者接收到了所有参与者的 ACK 响应后,发送 DoCommit 消息,参与者接收到 DoCommit 消息之后,正式提交事务,完成事务提交之后,释放所有锁住的资源,并且向协调者发送 ACK 响应,协调者收到所有参与者的 ACK 响应后完成事务。

2PC 与 3PC 算法都是基于 XA 协议,这个协议在商业数据库支持的比较好。这两种算法都属于支持 ACID 特性,数据特性为强一致性,执行方式为同步执行,但是两种算法都没有解决最后一个阶段因为网络抖动而导致的数据不一致问题,并且在并发性性能上表现不如接下来介绍的基于分布式消息的最终一致性方案。

2.3. 基于分布式消息的最终一致性方案

基于分布式消息的最终一致性方案解决分布式事物的一个思路就是:将需要分布式处理的事务通过消息或者日志的方式异步执行,消息或日志可以存到本地文件、数据库或消息队列中, 分布式系统中执行失败的地方可以通过业务规则进行失败重试。

基于分布式消息的最终一致性方案示意图

该方案大致步骤为:

  1. 每个业务处理服务在业务事务提交前,向实时消息服务(即消息中间件)请求发送消息;
  2. 实时消息服务只记录消息数据,而不发送至其他业务系统,消息状态标记为“待确认”,并且返回消息持久化结果(成功 / 失败);
  3. 若消息持久化返回成功,业务系统则进行业务事务提交,把操作结果(成功 / 失败)发送给消息中间件;
  4. 消息中间件收到业务操作结果,若是成功,则更新消息存储中的消息状态为“待发送(可发送)”,并执行消息投递,若是失败则删除消息存储中的消息并结束;
  5. 当消息为“可发送”状态时,MQ 则会把消息发送给其他业务系统,然后其他业务系统根据同样的步骤进行业务操作;

最后是分布式系统中当且仅当所有的业务系统事务均成功时整个流程才成功。

事物消息处理流程

可以看出,基于分布式消息的最终一致性方案是遵循 BASE 理论,异步执行,数据一致性为最终一致性的方案。

此外,如若消息者消费消息失败的话,MQ 自己会负责重推消息,直到消费成功,消费端的一致性是通过MQ的重试机制来完成的。

三、参考