分布式事务选型对比

分布式事务选型对比

随着互联网架构的不断扩展,传统的ACID事务已经无法满足要求。为了解决这个问题,BASE理论被提出来取代ACID,基于BASE理论和CAP理论的分布式事务也有各种事务策略。如何保证整个事务整体的原子性与一致性问题?分布式事务如何选项才能更加适合当下的业务场景?已经成为了当下事务的痛点。

解决方案对比

2PC两阶段提交

XA方案

为了提供统一的对接模型和标准,X/OPEN组织提出了一个DTP(Distributed Transaction Processing Reference Model)模型,其中规定了分布式事务中的三个角色:

  • AP:应用程序
  • TM:事务管理器,负责协调管理整个分布式事务
  • RM:资源管理器也就是事务参与者,负责控制分支事务

DTP模型还定义了TM和RM之间通信的接口规范,这个规范就是XA,本职上也是数据库提供的2PC接口协议。基于XA方案的事务流程如下:

  • AP 持有 D1 和 D2 两个数据源;
  • AP 通过 TM 通知 D1 的 RM 操作数据,同时通知 D2 的 TM 操作数据,此时 D1 和 D2 操作的数据锁定,RM 不提交事务;
  • TM 收到 两个数据源的 RM 执行恢复,只要有一方失败,则向另外的 RM 发起回滚指令,对应 RM 回滚分支事务释放资源锁;
  • 若均为成功,TM 向所有 RM 发起提交指令,所有 RM 接到指令提交事务,释放资源锁。

XA方案本质上是数据库层面的分布式事务,要求数据库支持事务,实现强一致性。在整个事务流程中,从准备阶段到第二阶段的commit或rollback的整个过程中,TM一直持有对应相关数据资源的锁,如果有其他事务要修改数据库的该条数据,就必须等待锁的释放,存在长事务风险。

结论

适合于本地的多事务组合,对于基于网络io的分布式事务,由于无法解决io丢失问题,因此并不可靠

TCC

关于TCC(Try-Confirm-Cancel)的概念,最早是由Pat Helland于2007年发表的一篇名为《Life beyond Distributed Transactions:an Apostate’s Opinion》的论文提出。 TCC事务机制相比于上面介绍的XA,解决了其几个缺点: 1.解决了协调者单点,由主业务方发起并完成这个业务活动。业务活动管理器也变成多点,引入集群。 2.同步阻塞:引入超时,超时后进行补偿,并且不会锁定整个资源,将资源转换为业务逻辑形式,粒度变小。 3.数据一致性,有了补偿机制之后,由业务活动管理器控制一致性

对于TCC的解释:

  • Try阶段:尝试执行,完成所有业务检查(一致性),预留必须业务资源(准隔离性)
  • Confirm阶段:确认执行真正执行业务,不作任何业务检查,只使用Try阶段预留的业务资源,Confirm操作满足幂等性。要求具备幂等设计,Confirm失败后需要进行重试。
  • Cancel阶段:取消执行,释放Try阶段预留的业务资源 Cancel操作满足幂等性Cancel阶段的异常和Confirm阶段异常处理方案基本上一致。

举个简单的例子如果你用100元买了一瓶水, Try阶段:你需要向你的钱包检查是否够100元并锁住这100元,水也是一样的。

如果有一个失败,则进行cancel(释放这100元和这一瓶水),如果cancel失败不论什么失败都进行重试cancel,所以需要保持幂等。

如果都成功,则进行confirm,确认这100元扣,和这一瓶水被卖,如果confirm失败无论什么失败则重试(会依靠活动日志进行重试)

对于TCC来说适合一些:

  • 强隔离性,严格一致性要求的活动业务。
  • 执行时间较短的业务

结论

TCC的特点在于业务资源检查与加锁,一阶段进行校验,资源锁定,如果第一阶段都成功,二阶段对锁定资源进行交易逻辑,否则,对锁定资源进行释放。应用实施成本较高。

MQ事务

RocketMQ在4.3版本开始支持事务消息,即prepare消息。这种消息在发送到MQ之后不会立即投递到消费者,会由发送者二次确认后,由RocketMQ根据确认指令决定投递还是丢弃。基于该方案的整个事务流程如下:

  • 事务发起方发送prepare消息到RocketMQ,RocketMQ存储该消息并反馈发起方消息接收成功,prepare消息不允许被消费者消费。
  • 发起方接收到RocketMQ的反馈后,执行本地事务,若本地事务执行成功,发起方向RocketMQ发送prepare消息的commit指令,否则发送rolback。
  • RocketMQ接收到发起方二次对prepare消息的确认指令后,执行消息的对应操作,commit控制消息进入实际的消费队列,rollback会删除对应消息,至此,保证了本地事务与消息发送的原子性问题。
  • 对于事务发起方二次确认的可靠性问题,RocketMQ提供反向的定时任务汇差事务状态机制,最多重试15次,超过则丢弃消息。
  • 事务参与者监听RocketMQ,基于消息确认(ACK)机制,参与者接收到消息并业务处理完成后向RocketMQ发送ack,保证参与者接收消息的可靠性问题。
  • 事务参与者对事务消息的消费方法实现幂等性,解决可能存在的消息重复消费问题。

结论

适合非主要流程的事务处理,如电商中的评论,物流信息等。允许丢失部分信息,必要时可人工干预。

saga事务

Saga是30年前一篇数据库伦理提到的一个概念。其核心思想是将长事务拆分为多个本地短事务,由Saga事务协调器协调,如果正常结束那就正常完成,如果某个步骤失败,则根据相反顺序一次调用补偿操作。 Saga的组成:

每个Saga由一系列sub-transaction Ti 组成 每个Ti 都有对应的补偿动作Ci,补偿动作用于撤销Ti造成的结果,这里的每个T,都是一个本地事务。 可以看到,和TCC相比,Saga没有“预留 try”动作,它的Ti就是直接提交到库。

Saga的执行顺序有两种:

T1, T2, T3, …, Tn

T1, T2, …, Tj, Cj,…, C2, C1,其中0 < j < n Saga定义了两种恢复策略:

向后恢复,即上面提到的第二种执行顺序,其中j是发生错误的sub-transaction,这种做法的效果是撤销掉之前所有成功的sub-transation,使得整个Saga的执行结果撤销。 向前恢复,适用于必须要成功的场景,执行顺序是类似于这样的:T1, T2, …, Tj(失败), Tj(重试),…, Tn,其中j是发生错误的sub-transaction。该情况下不需要Ci。

这里要注意的是,在saga模式中不能保证隔离性,因为没有锁住资源,其他事务依然可以覆盖或者影响当前事务。

还是拿100元买一瓶水的例子来说,这里定义

T1=扣100元 T2=给用户加一瓶水 T3=减库存一瓶水

C1=加100元 C2=给用户减一瓶水 C3=给库存加一瓶水

我们一次进行T1,T2,T3如果发生问题,就执行发生问题的C操作的反向。 上面说到的隔离性的问题会出现在,如果执行到T3这个时候需要执行回滚,但是这个用户已经把水喝了(另外一个事务),回滚的时候就会发现,无法给用户减一瓶水了。这就是事务之间没有隔离性的问题

可以看见saga模式没有隔离性的影响还是较大,可以参照华为的解决方案:从业务层面入手加入一 Session 以及锁的机制来保证能够串行化操作资源。也可以在业务层面通过预先冻结资金的方式隔离这部分资源, 最后在业务操作的过程中可以通过及时读取当前状态的方式获取到最新的更新。

结论

Saga的核心就是补偿,一阶段就是服务的正常顺序调用(数据库事务正常提交),如果都执行成功,则第二阶段则什么都不做;但如果其中有执行发生异常,则依次调用其补偿服务(一般多逆序调用未已执行服务的反交易)来保证整个交易的一致性。应用实施成本一般。


分布式事务选型对比
https://note.0moe.cn/数据库/2021/01/13/分布式事务选型对比/
作者
Dawn_南风
发布于
2021年1月13日
许可协议