分布式事务二

分布式事务概述

Posted by Jason Lee on 2022-04-01

基础理论

分布式事务

单机的事务我已经在我的隔离级别系列文章中总结过,分布式事务是针对分布式系统中对数据的修改发生的逻辑上满足事务特性的操作序列的要求。

分布式事务指的是事务的参与者、事务所在的服务器、涉及的资源服务器以及事务管理器等分别位于不同分布式系统的不同服务或数据库节点上。简单来说,分布式事务就是一个在不同环境(比如不同的数据库、不同的服务器)下运行的整体事务。这个整体事务包含一个或者多个分支事务,并且整体事务中的所有分支事务要么全部提交成功,要么全部提交失败。

例如,在电商系统的下单减库存业务中,订单业务所在的数据库为事务A的节点,库存业务所在的数据库为事务B的节点。事务A和事务B组成了一个具备ACID特性的分布式事务,要么全部提交成功,要么全部提交失败。

刚性事务和柔性事务

  • 刚性事务满足ACID理论
    • DTP模型(XA规范,2PC, 3PC协议)
  • 柔性事务满足BASE理论(基本可用,最终一致)
  • 柔性事务分为:
    • 两阶段型
    • 补偿型
    • 异步确保型
    • 最大努力通知型

强一致性解决方案

DTP 模型

DTP全称是Distributed Transaction Process,即分布式事务模型。该模型是X/Open组织定义的一套分布式事务标准,这套标准主要定义了实现分布式事务的规范和API,具体的实现则交给相应的广商来实现。

  • AP(ApplicationProgram): 应用程序,例如:订单服务、库存服务
  • RM(Resource Manager): 资源管理器,可以理解为一个数据库,AP通过RM对资源控制,资源必须实现XA定义的接口
  • TM(Transaction Manager): 事务管理器,负责分配事务的唯一标识,监控事务执行进度,负责事务的提交与回滚

XA规范

在DTO模型中,我们先来了解一下XA规范。DTP分布式事务模型中,XA规范定义了RM-TM交互的接口(TM与数据库之间的接口规范),TM用它来通知数据库事务的开始结束以及提交回滚等。XA接口函数有数据库厂商提供(mysql、oracle)

2PC 模型(协议)

二阶段提交(Two-phaseCommit 即 2PC)是指,在计算机网络以及数据库领域内,为了使基于分布式系统架构下的所有节点在进行事务提交时保持一致性而设计的一种算法(Algorithm)。通常,二阶段提交也被称为是一种协议(Protocol))。

在分布式系统中,每个节点虽然可以知晓自己的操作时成功或者失败,却无法知道其他节点的操作的成功或失败。当一个事务跨越多个节点时,为了保持事务的ACID特性,需要引入一个作为协调者的组件来统一掌控所有节点(称作参与者)的操作结果并最终指示这些节点是否要把操作结果进行真正的提交(比如将更新后的数据写入磁盘等等)。

因此,二阶段提交的算法思路可以概括为:

  • 1、参与者将操作成败通知协调者;
  • 2、再由协调者根据所有参与者的反馈情报决定各参与者是否要提交操作还是中止操作。

所谓的两个阶段是指:

  • 第一阶段:准备阶段(投票阶段)
  • 第二阶段:提交阶段(执行阶段)

1、准备阶段

事务协调者(事务管理器)给每个参与者(资源管理器)发送Prepare消息,每个参与者要么直接返回失败(如权限验证失败),要么在本地执行事务,写本地的redo和undo日志,但不提交,到达一种“万事俱备,只欠东风”的状态。

可以进一步将准备阶段分为以下三个步骤:

  • 1)协调者节点向所有参与者节点询问是否可以执行提交操作(vote),并开始等待各参与者节点的响应。
  • 2)参与者节点执行询问发起为止的所有事务操作,并将Undo信息和Redo信息写入日志。(注意:若成功这里其实每个参与者已经执行了事务操作)
  • 3)各参与者节点响应协调者节点发起的询问。如果参与者节点的事务操作实际执行成功,则它返回一个”同意”消息;如果参与者节点的事务操作实际执行失败,则它返回一个”中止”消息。

2、 提交阶段

如果协调者收到了参与者的失败消息或者超时,直接给每个参与者发送回滚(Rollback)消息;否则,发送提交(Commit)消息;参与者根据协调者的指令执行提交或者回滚操作,释放所有事务处理过程中使用的锁资源。(注意:必须在最后阶段释放锁资源)

接下来分两种情况分别讨论提交阶段的过程。

成功提交

当协调者节点从所有参与者节点获得的相应消息都为”同意”时:

  • 1)协调者节点向所有参与者节点发出”正式提交(commit)”的请求。
  • 2)参与者节点正式完成操作,并释放在整个事务期间内占用的资源。
  • 3)参与者节点向协调者节点发送”完成”消息。
  • 4)协调者节点受到所有参与者节点反馈的”完成”消息后,完成事务。

回滚提交

如果任一参与者节点在第一阶段返回的响应消息为”中止”,或者 协调者节点在第一阶段的询问超时之前无法获取所有参与者节点的响应消息时:

  • 1)协调者节点向所有参与者节点发出”回滚操作(rollback)”的请求。
  • 2)参与者节点利用之前写入的Undo信息执行回滚,并释放在整个事务期间内占用的资源。
  • 3)参与者节点向协调者节点发送”回滚完成”消息。
  • 4)协调者节点受到所有参与者节点反馈的”回滚完成”消息后,取消事务。

不管最后结果如何,第二阶段都会结束当前事务。

2PC协议的问题

值得注意的是,2PC模型存在着如下的缺点。

  • 1)同步阻塞问题:事务的执行过程中,所有参与事务的节点都会对其占用的公共资源加锁,导致其他访问公共资源的进程或者线程阻塞。

本地事务在prepare阶段锁定资源,比如账户服务要扣减xiaoming这个账户的金额100元,那必须把xiaoming这个账户先锁定。这样如果有其他事务也要修改xiaoming这个账户,就必须等待前面的事务完成。这样就造成了延迟和性能下降。

  • 2)单点故障问题:如果事务管理器发生故障,则资源管理器会一直阻塞。

协调节点是单节点的,如果发生故障,整个事务会一直阻塞。比如第一个阶段prepare成功了,但是第二个阶段协调节点发出commit指令之前宕机了,所有服务的数据资源处于锁定状态,后面的事务只能等待。

  • 3)数据不一致问题:如果在Commit阶段,由于网络或者部分资源管理器发生故障,导致部分资源管理器没有接收到事务管理器发送过来的Commit消息,会引起数据不一致的问题。

如果第一阶段prepare成功了,但是第二阶段commit的时候,如果协调节点通知库存服务失败了,这样就相当于生成了订单,扣减了账户,但是没有扣减库存。这导致了数据的不一致。

3PC 模型(协议)

3PC模型是指三阶段提交模型,是在2PC模型的基础上改进的版本。3PC模型把2PC模型中的Prepare阶段一分为二,最终形成3个阶段:CanCommit阶段、PreCommit阶段和 doCommit或者 doRollback 阶段。3PC模型的流程同样分为事务执行成功和事务执行失败两种情况。

CanCommit阶段

3PC的CanCommit阶段其实和2PC的准备阶段很像。协调者向参与者发送catCommit请求,参与者如果可以提交就返回Yes响应,否则返回No响应。

  • 1.事务协调者(Coordinator)向参与者(Partcipant)发送CanCommit请求。询问是否可以执行事务提交操作。然后开始等待参与者的响应。

  • 2.响应反馈:参与者接到CanCommit请求之后,正常情况下,如果其自身认为可以顺利执行事务,则返回Yes响应,并进入预备状态。否则反馈No。

PreCommit阶段

协调者(Coordinator)根据参与者(Partcipant)的反应情况来决定是否可以记性事务的PreCommit操作。根据响应情况,有以下两种可能。

  1. 假如协调者从所有的参与者获得的反馈都是Yes响应,那么就会执行事务的预执行。
  2. 发送预提交请求: 协调者向参与者发送PreCommit请求,并进入Prepared阶段。
  3. 事务预提交 :参与者接收到PreCommit请求后,会执行事务操作,并将undo和redo信息记录到事务日志中。

  1. 响应反馈: 如果参与者成功的执行了事务操作,则返回ACK响应,同时开始等待最终指令。假如有任何一个参与者向协调者发送了No响应,或者等待超时之后,协调者都没有接到参与者的响应。那么就执行事务的中断。发送中断请求: 协调者向所有参与者发送abort请求。
  2. 中断事务: 参与者收到来自协调者的abort请求之后(或超时之后,仍未收到协调者的请求),执行事务的中断。

doCommit阶段

该阶段进行真正的事务提交,也可以分为以下两种情况。

执行提交

  1. 发送提交请求: 协调接收到参与者发送的ACK响应,那么他将从预提交状态进入到提交状态。并向所有参与者发送doCommit请求。
  2. 事务提交: 参与者接收到doCommit请求之后,执行正式的事务提交。并在完成事务提交之后释放所有事务资源。
  3. 响应反馈: 事务提交完之后,向协调者发送Ack响应。
  4. 完成事务: 协调者接收到所有参与者的ack响应之后,完成事务。

中断事务。

协调者没有接收到参与者发送的ACK响应(可能是接受者发送的不是ACK响应,也可能响应超时),那么就会执行中断事务。

  • 发送中断请求: 协调者向所有参与者发送abort请求
  • 事务回滚: 参与者接收到abort请求之后,利用其在阶段二记录的undo信息来执行事务的回滚操作,并在完成回滚之后释放所有的事务资源。
  • 反馈结果 :参与者完成事务回滚之后,向协调者发送ACK消息
  • 中断事务: 协调者接收到参与者反馈的ACK消息之后,执行事务的中断。

在doCommit阶段,如果参与者无法及时接收到来自协调者的doCommit或者rebort请求时,会在等待超时之后,会继续进行事务的提交。(其实这个应该是基于概率来决定的,当进入第三阶段时,说明参与者在第二阶段已经收到了PreCommit请求,那么协调者产生PreCommit请求的前提条件是他在第二阶段开始之前,收到所有参与者的CanCommit响应都是Yes。一旦参与者收到了PreCommit,意味他知道大家其实都同意修改了)

所以,一句话概括就是,当进入第三阶段时,由于网络超时等原因,虽然参与者没有收到commit或者abort响应,但是他有理由相信:成功提交的几率很大。 )

增加了一个询问阶段,询问阶段可以确保尽可能早的发现无法执行操作而需要中止的行为,但是它并不能发现所有的这种行为,只会减少这种情况的发生在准备阶段以后,协调者和参与者执行的任务中都增加了超时,一旦超时,协调者和参与者都继续提交事务,默认为成功,这也是根据概率统计上超时后默认成功的正确性最大。

三阶段提交协议与两阶段提交协议相比,具有如上的优点,但是一旦发生超时,系统仍然会发生不一致,只不过这种情况很少见罢了,好处就是至少不会阻塞和永远锁定资源。

区别和联系

概念 解释
DTP模型 规定了分布式事务中的角色模型,并在其中指定了全局事务的控制需要使用 2PC 协议来保证数据的一致性。
2/3PC 二/三阶段提交,是计算机网络尤其是数据库领域内,为了保证分布式系统架构下所有节点在进行事务处理过程中能够保证原子性和一致性而设计的一种算法。同时,2PC 也被认为是一种一致性协议,用来保证分布式系统数据的一致性
XA 规范 X/Open 组织提出的分布式事务处理规范,XA 规范定义了 2PC(两阶段提交协议)中需要用到的接口,也就是上图中 RM 和 TM 之间的交互。2PC 和 XA 两者最容易混淆,可以这么理解,DTP 模型定义 TM 和 RM 之间通讯的接口规范叫 XA,然后 关系数据库(比如MySQL)基于 X/Open 提出的的 XA 规范(核心依赖于 2PC 算法)被称为 XA 方案

参考



支付宝打赏 微信打赏

赞赏一下