2022-05-24

2022-05-24  本文已影响0人  冬狮郎

分布式事务

2PC

Two-Phase Commit缩写。二阶段提交。为了使基于分布式系统架构下的所有节点在进行事务处理过程中能够保持原子性、一致性而设计的一种算法。目前绝大部分的关系型数据库都是采用二阶段提交协议来完成分布式事务处理的。

[图片上传失败...(image-91bb3e-1653381325874)]

二阶段提交协议是将事务的提交过程分为两个阶段来进行处理:

阶段一:提交事务请求

1、事务询问。

协调者向所有的参与者发送事务内容,询问是否可以执行事务提交操作,并开始等待各参与者的响应。

2、执行事务。

各参与者节点执行事务操作,并将undo和redo信息记入事务日志中。

3、各参与者向协调者反馈事务询问的响应。

如果参与者成功执行了事务操作,那么反馈给协调者Yes响应,表示事务可以执行;

如果参与者没有成功执行事务,那么就反馈给协调者No响应,表示事务不可以执行。

上面内容在形式上近似是协调者组织各参与者对一次事务操作的投票表态过程。因此,二阶段提交协议的阶段一也被称为“投票阶段”。即各参与者投票表名是否要继续执行接下去的事务提交操作。

有点类似于,扣减库存业务之前的,预占逻辑。

阶段二:执行事务提交

协调者会根据各参与者的反馈情况来决定最终是否可以进行事务提交操作。正常情况下,包含以下两种可能:

1、执行事务提交

如果协调者从所有的参与者获得的反馈都是Yes响应,那么就会执行事务提交。

1)发送提交请求

协调者向所有参与者节点发起Commit请求。

2)事务提交

参与者接受到Commit请求后,会正式执行事务提交操作,并在完成提交之后释放在整个事务执行期间占用的事务资源。

3)反馈事务提交结果

参与者在完成事务提交之后,向协调者发送Ack消息。

4)完成事务

协调者接受到所有参与者反馈的Ack消息后,完成事务。

2、中断事务

假如任何一个参与者向协调者反馈了No响应,或者在等待超时后,协调者尚无法接收到所有参与者的反馈响应,那么就会中断事务。

1)发送回滚请求

协调者向所有参与者节点发出Rollback请求。

2)事务回滚

参与者接受到Rollback请求后,会利用其在阶段一中记录的Undo信息来执行事务回滚操作,并在完成回滚之后释放在整个事务执行期间占用的资源。

3)反馈事务回滚结果

参与者在完成事务回滚之后,向协调者发送Ack消息。

4)中断事务

协调者接受到所有参与者反馈的Ack消息后,完成事务中断。

优缺点

优点

原理简单、实现方便;

缺点

有点类似于,真正去执行扣减逻辑(事务提交)或释放占用(事务回滚)

开启事务 -> 阶段一 -> 阶段二 -> 本地提交/回滚事务。

引用:

《从Paxos到Zookeeper分布式一致性原理与实践》

3PC

3PC,Three-Phase Commit,三阶段提交,是2PC的改进,将二阶段提交协议的“提交事务请求”过程一分为二,形成了由CanCommit、PreCommit和DoCommit三个阶段组成的事务处理协议。

[图片上传失败...(image-bf557a-1653381325874)]

阶段一:CanCommit

1、事务询问

协调者向所有的参与者发送一个包含事务内容的canCommit请求,询问是否可以执行事务提交操作,并开始等待各参与者的响应。

2、各参与者向协调者反馈事务询问的响应

参与者在接受到来自协调者的canCommit请求后,正常情况下,如果其自身认为可以顺利执行事务,那么会反馈Yes响应,并进入预备状态,否则反馈No响应。

阶段二:PreCommit

协调者会根据各参与者的反馈情况来决定是否可以进行事务的PreCommit操作。正常情况下,包含两种情况:

1、执行事务预提交

2、中断事务

阶段三:doCommit

该阶段会进行真正的事务提交。

1、执行提交

2、中断事务

参与者向协调者反馈No响应,或协调者无法收到所有参与者的反馈响应,就会中断事务。

优缺点

3PC把2PC的第一阶段分拆成两个阶段,减少了阶段一对事务的阻塞时间。

阶段一can? -> 开启事务 -> 阶段2pre. -> 阶段3do! -> 提交事务;

整体感觉如:预占库存前,需要先校验单据状态、数据的有效性等。3PC像是把这一阶段的工作拆分成阶段一。后续阶段二理解为占用库存,阶段3为扣减或释放库存。

引用:

《从Paxos到Zookeeper分布式一致性原理与实践》

基于消息的分布式事务

ebay架构师发表的的文章:Base: An Acid Alternative。核心思想为:通过牺牲强一致性来获得可用性,并允许数据在一段时间内是不一致的,但最终达到一致性。实现手段为:将需要分布式系统执行的任务经过消息或日志的方式来异步执行。消息或日志能够存储到本地文件、数据库或消息队列,可以进行失败重试

所以本方式的理论依据为:BASEBasically Available,Soft state,Eventually consistentWALWrite- Ahead Logging

基于消息的分布式事务模型主要有两种解决方案:

事务消息

[图片上传失败...(image-f3bde3-1653381325874)]

1)事务发起者预先发送事务消息。

2)MQ 收到事务性消息后,将消息持久化,将消息状态更新为“待发送”,并向发送方发送确认(ACK)消息。

3)如果事务发起者没有收到 ACK 消息,则取消本地事务的执行。如果事务发起者收到ACK消息,则执行本地事务,并发送另一条消息到MQ系统,通知本地事务的执行。

4)MQ 收到通知后,会根据本地事务的执行结果改变事务的消息状态。如果执行成功,MQ 将消息状态更改为“consumable”并将其交付给订阅者。如果执行失败,则删除该消息。

5)当执行本地事务时,发送到 MQ 的通知消息可能会丢失。因此,支持事务性消息的 MQ 具有定期的扫描逻辑。通过扫描,MQ 识别出处于“待发送”状态的消息,并向消息的发送者发起查询,以了解消息的最终状态。MQ 根据查询结果相应地更新消息状态。因此,事务的发起者需要为MQ系统提供查询事务消息状态的接口。

6)如果事务消息的状态是“准备发送”,MQ 将消息推送给下游参与者。如果推送失败,系统将继续重试。

7)收到消息后,下游参与者执行本地事务。如果本地事务执行成功,则向 MQ 系统发送 ACK 消息。如果执行失败,则不发送 ACK 消息。在这种情况下,MQ 会不断地向不返回 ACK 消息的下游参与者推送消息。

[图片上传失败...(image-5e53ee-1653381325874)]

客户端发送MQ事务消息,接受到ack后开始执行本地事务。客户端将本地事务执行结果通知MQ。

客户端要提供查询事务消息状态的接口,通知失败后MQ可以主动查询。

若成功,MQ将消息推送给下游,否则将消息丢弃。

如果推送失败,则不断重试,直到成功。

本地消息

事务发起者维护一个本地消息表。业务和本地消息表操作在同一个本地事务中执行。如果服务执行成功,本地消息表中也会记录一条状态为“待发送”的消息。系统启动定时任务,定期扫描本地消息表中处于“待发送”状态的消息发送给MQ。如果发送失败或超时,消息将被重新发送,直到发送成功。然后,该任务将从本地消息表中删除状态记录。后续的消费和订阅过程与事务性消息模式类似。

[图片上传失败...(image-b5e418-1653381325874)]

阶段一:消息的入队

开启事务写操作后,进行消息的发送或存储,全部成功后提交事务。

核心理念:让消息的入队操作与transaction的写操作通过一个本地事务处理。最简单的方式就是用一个消息表来存储这些消息。这个消息表和transaction在同一个数据库。

begin transaction
  insert into transaction(xid, seller_id, buyer_id, amount);
  //入队卖家数据更新消息,第一个字段是balance,第二个字段是id
  queue message "update user("seller", seller_id, amount)"; 
  //入队买家数据更新消息,第一个字段是balance,第二个字段是id
  queue message "update user("buyer", buyer_id, amount)"; 
end transaction

阶段二:消息的出队

接收到消息后先进行唯一性/幂等校验。开启事务后存储消息并顺序执行事务写操作。

核心理念:

1、需要保证此阶段事务操作的幂等性;(引入update_applied表解决问题)

2、能够跟踪业务记录执行到哪个阶段。(引入update_applied表,幂等规避此问题。)

3、需要保证消息消费的顺序性(防止状态/数据回滚、乱序执行)。(update_applied引入版本号等)

for each message in queue
  peek message
  begin transaction
    //如果当前消息在update_applied表中有记录,说明已经处理过了,否则没有处理过
    select count(*) from update_applied as processed where trans_id = message.trans_id 
      and balance = message.balance and user_id = message.user_id; 
    if proccessed == 0
      if message.balance == "seller"
        //增加了last_purchase和trans_date的比较
        update user set amt_sold = amt_sold + messgae.amount, last_purchase = message.trans_date
          where id = message.seller_id and last_purchase < message.trans_date
      else
        //增加了last_sale和trans_date的比较
        update user set amt_bought = amt_bought + message.amount, last_sale = message.trans_date
          where id = message.buyer_id and last_sale < message.trans_date
      end if
      //插入记录,表示这条消息处理过了
      insert into updates_applied(message.trans_id, message.balance, message.user_id);
    end if
  end transaction
  if transaction successful
    //如果上面的事务成功处理,则把消息从队列删除。如果这个操作失败也没关系,上面的事务已经变成幂等操作了
    remove message from queue; 
  end if
end for

优缺点

引用:

1、分布式事务系列一:BASE,一种ACID的替代方案(eBay分布式事务解决方案)

2、知乎:常用的分布式事务解决方案

3、分布式事务解决方案的深入分析

4、RocketMQ的分布式事务(半消息事务)

XA规范

分布式事务解决方案的深入分析

最早的分布式事务产品可能是 AT&T 在 1980 年代推出的 Tuxedo(Transactions for UNIX,Extended for Distributed Operations)。Tuxedo 最初是作为电信领域 OLTP 系统的分布式事务中间件开发的。后来标准化组织X/Open采用了Tuxedo的设计和一些接口,引入了分布式事务规范,XA规范。

四个核心角色

XA 规范定义了一个分布式事务处理模型,包括四个核心角色:

下图显示了 XA 规范中定义的事务模型。发起分布式事务的TM实例称为根节点,其他TM实例统称为事务参与者。发起者启动一个全局事务,事务参与者运行自己的事务分支。如果一个 TM 实例向其他 TM 实例发起服务调用,则发起者为上级节点,而被调用的实例为下级节点。

[图片上传失败...(image-af89ea-1653381325874)]

事务处理过程

在 XA 规范中,分布式事务建立在 RM 的本地事务之上,然后被视为分支事务。TM 负责协调这些分支事务并确保它们全部成功提交或全部回滚。XA 规范将分布式事务处理过程分为以下两个阶段,因此也称为两阶段提交协议:

1)准备阶段

TM 记录事务启动并查询每个 RM 是否准备好执行准备操作。

RM 收到订单后,会评估自己的状态,并尝试为本地事务执行准备操作,例如预留资源、锁定资源和执行操作。然后,RM 等待来自 TM 的后续订单而不提交事务。如果前面的尝试失败,RM 会通知 TM 该阶段的执行失败并回滚已执行的操作。然后,RM 不再参与该交易。例如,MySQL 会在这个阶段锁定资源并写入 redo 和 undo 日志。

TM 收集 RM 的响应并将事务准备记录为已完成。

2) 提交或回滚阶段

本阶段根据前一阶段的协调结果发起事务提交或回滚操作。

如果所有 RM 在上一步中都响应成功,则执行以下操作:

如果任何 RM 在上一步中响应执行失败或没有及时响应,则 TM 将事务视为失败。然后,将执行以下操作:

[图片上传失败...(image-dc8241-1653381325874)]

XA 规范还定义了以下优化措施:

XA 规范详细定义了核心组件之间的交互接口。以 TM 与 RM 的交互接口为例。下图是一个完整的全局事务,其中 TM 和 RM 的交互非常频繁。

[图片上传失败...(image-78b9a0-1653381325874)]

异常处理

交易执行期间可能会发生错误和网络超时。对于这些异常,不同的实现可能会有不同的异常处理方法,如下所述。

这段有点像Saga Log的策略

优缺点

优点:

XA 规范是最早的分布式事务规范。Oracle、MySQL、SQL Server等主流数据库产品都支持XA规范。请注意,J2EE 中的 JTA 规范也是基于 XA 规范,因此与 XA 规范兼容。

XA 是在资源管理层实现的分布式事务模型。它的特点是对企业的侵入程度低。

缺点:

XA 的两阶段提交协议可以覆盖分布式事务的三种场景。但是,RM 在执行全局事务期间会一直锁定资源。如果事务涉及的 RM 过多,特别是在跨业务场景下,网络通信的数量和时间消耗会迅速增加。从而导致阻塞时间延长,系统吞吐量下降,事务死锁概率增加。因此,两阶段提交协议不适用于微服务场景下的跨服务分布式事务模式。

每个 TM 领域都会创建一个单点,这可能会导致单点故障。如果 TM 在第一阶段之后崩溃,参与的 RM 将不会收到第二阶段的订单,因此会长时间持有资源锁。因此,这会影响业务的吞吐量。另一方面,在一个完整的全局事务中,TM 与 RM 交互 8 次,导致复杂性和性能下降。

此外,两阶段协议可能会导致脑裂异常。如果 TM 在第二阶段指示 RM 提交事务后出现故障,并且只有部分 RM 收到提交顺序,那么当 TM 恢复时,它无法协调所有 RM 维护本地事务的一致性。

XA 必须处理许多异常情况,这对框架的实现具有挑战性。关于开源实现,可以参考 Atomikos 和 Bitronix。

针对两阶段提交协议中存在的问题,提出了一种改进的三阶段提交方案。这种新的解决方案消除了单点故障 (SPOF),并为 RM 添加了超时机制,以避免长期锁定资源。然而,三相解决方案无法解决裂脑问题,很少应用于实际案例。如果您对此解决方案感兴趣,可以阅读相关资料。

TCC

git 开源

SAGA

https://zhuanlan.zhihu.com/p/95852045

https://baijiahao.baidu.com/s?id=1709259416203967205&wfr=spider&for=pc

https://opentalk.upyun.com/310.html

https://blog.csdn.net/qq_42046105/article/details/114985125

1、1987年发表的Sagas论文Hector & Kenneth - Sagas _ Department_of_Computer_Science

2、2015年发布的Sagas论文Caitie,McCaffrey - Distributed Sagas

3、Chris Richardson发表的Sagas文章:Pattern:Saga

Saga为解决可能会长时间运行的分布式事务(Long-Lived Transaction长活事务)问题。将长活事务分解成可以交错运行的子事务集合。其中每个子事务都是一个保持数据库一致性的真实事务。属于是一种纯业务补偿模式。当一个服务失败的时候,其所有依赖的上游服务都进行业务补偿操作。

Saga中的事务相关联,作为非原子单位执行。任何未完成执行的Saga是不满足要求。如果发生,必须得到补偿,要修正未完成执行的部分。每个Saga子事务应提供对应的补偿事务。

Saga事务恢复策略

理论上,补偿事务永不失败。但是在复杂的真实生产环境中,服务器可能会宕机,网络会抖动,甚至数据中心会停电。这种情况下,最后的手段是提供回退措施,比如人工干预。

向前恢复 forward recovery

重试失败的事务,假设每个子事务最终都会成功。适用于必须要成功的场景。不需要提供补偿事务。在某种情况下更符合我们的需求。

T1,T2,...,Tj(失败),Tj(重试),...,Tn

向后恢复 backward recovery

如果任一子事务失败,补偿所有已完成的事务。这样最终撤销掉之前所有成功的sub-transaction,使得整个Saga的执行结果撤销。

T1, T2, ..., Tj, Cj,..., C2, C1,其中0 < j < n

Saga的使用条件

补偿也有需要考虑的事项:

但是这对我们的业务来说不是问题。难以撤销的行为也可能被补偿。例如发送电邮的事务可以通过发送解释问题的另一封电邮来补偿。

Saga Log

Saga保证所有的子事务都得以完成或补偿,但Saga系统本身也会崩毁。其崩溃时可能处于下面几个状态:

为了恢复到上面状态,我们需要追踪子事务及补偿事务的每一步。可以通过事件的方式达到以上要求,并将事件保存在名为saga log的持久存储中:

[图片上传失败...(image-43e6d-1653381325874)]

通过将这些事件持久化到saga中,我们可以将saga恢复到上述任何状态。

Saga分布式事务协调

阿里巴巴的开源项目 Seata和华为的开源项目 ServiceComb 都支持 Saga。

参考

1、微服务场景下的数据一致性解决方案

2、

上一篇 下一篇

猜你喜欢

热点阅读