分布式事务解决方案(Saga)

分布式事务就是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上。

简单的说,就是一次大的操作由不同的小操作组成,这些小的操作分布在不同的服务器上,且属于不同的应用,分布式事务需要保证这些小操作要么全部成功,要么全部失败。

本质上来说,分布式事务就是为了保证不同数据库的数据一致性。

这一问题在我司这种分布式系统架构下尤为突出。


本文阐述对于长事务,需要实时获取响应的,强一致的分布式事务解决方案。

介绍

事务的 saga 模式是一种分布式事务处理的模式,用于管理跨多个服务的事务。在这种模式下,每个服务都有自己的事务,这些事务通过一系列的相互协调和妥协来保持一致性。

在 saga 模式中,事务被分为多个步骤,并且每个步骤都可以被回滚。如果在某个步骤失败,那么整个事务将被回滚到之前的状态,并执行逆向操作以确保数据的一致性。

Saga 模式使用异步消息传递来协调服务之间的交互。当一个服务完成一个步骤并准备好进行下一个步骤时,它会发布一个消息通知其他服务。每个服务都会订阅这些消息并执行必要的操作。

Saga 模式的优点是它可以处理大规模、复杂的事务,同时提供高可用性和容错性。缺点是它可能需要更复杂的编程模型和管理复杂性。

常见方案

(不含MQ、本地消息表等最终一致方案)

嵌入式组件(Saga) 独立中间件(Saga) 开源seata(AT) 其他TCC方案
方案简介 saga模式作为插件,嵌入到业务系统中,需要在业务系统中加入一张事务事件表;由业务发起方(组件)同时作为些协调者,协调整个事务 saga模式由独立中间件代替嵌入式的组件 阿里开源,自动生成回滚sql,开发无感知 TCC: 复杂度比SAGA更高,需要提供try接口、confirm接口和cancle接口,目前主流开源的方案都是只支持JAVA。此处不展开
优点 Saga模式本身容易理解轻量,只依赖数据库 Saga模式本身容易理解支持java和php对业务系统数据库压力小 侵入性低开发人员基本无感知 -
缺点 - 只能JAVA应用作为事务发起方- 回滚接口需要幂等;- 嵌入在业务系统中,占用业务系统的应用和数据库资源- 需要使用系统表,记录所有子事务操作状态,可能影响系统数据库的性能;- java、php要配套开发组件- 对开发人员有要求,需要注意事务边界,本地事务最好放在最后 - 回滚接口需要幂等;- 事务发起方嵌入一张事件表,记录本地事务状态- RPC调用性能- 可用性风险- 对开发人员有要求,需要注意事务边界,本地事务最好放在最后 - 只支持Java- 本地RM为本次事务影响的数据生成回滚日志,回滚日志包含了数据的前镜像和后镜像,这意味着,事务影响的数据越多,回滚日志越大(两倍)。- 回滚日志存放在本地数据库中(redo_log表)- 使用简单,容易滥用分布式事务,导致对TC的资源浪费、争抢

综合比较后,考虑到侵入性、复杂度和对PHP的支持度,决定采用Saga模式,并用中间件作为事务的协调器。

Saga模式

Saga模型将一个分布式事务拆分为多个本地事务,每个本地事务都有相应的正向执行模块(Transaction)和逆向回滚模块(Compensation)。

任何一个本地事务出错时,都可以通过调用相关的逆向回滚实现事务的最终一致性。

分布式事务解决方案(Saga)

适用场景:

  • 业务流程长、业务流程多
  • 参与者系统类型较多,无法提供 TCC 模式要求的三个接口

比如:

  • 下单场景

客户下单时,需要扣减库存,使用优惠券、扣减积分,最后生成订单,

这个流程涉及订单、库存、促销、积分等多个服务,是典型的长事务,且要求数据一致的场景

任一环节发生错误,将导致下单失败。

如:积分扣减失败,最终订单无法下单,已扣减库存和已使用的优惠券需要回滚!

优势:

  • 一阶段提交本地事务,无锁,高性能
  • 事件驱动架构,参与者可异步执行,高吞吐
  • 补偿服务易于实现

缺点:

  • 不保证隔离性

Saga中间件技术方案

为支持java和Php服务的分布式事务,提供独立的中间件作为分布式事务的协调器。

中间件服务判断事务的结果(成,失败,超时等等),并负责对失败事务进行回滚。

分布式事务解决方案(Saga)

回滚接口由事务参与方提供。

  • 中间件实现高可用
  • 中间件提供分布式事务操作的相关接口(HTTP协议)
  • 提供框架/组件自动完成和中间件的交互功能
  • 事务发起方需要内嵌一张事务事件表,记录事务的处理结果,发起方和中间件出现异常情况时,中间件事后可通过接口查询事务结果。

分布式事务解决方案(Saga)

分布式事务范围定义

推荐的分布式事务范围是以一次本地事务的提交作为终点。

如此界定的好处是,事务发起者(MethodA)不需要提供回滚接口。(事务发起者的业务逻辑往往最复杂,回滚难度更大)

  • 本地事务的成功执行,则标识整体分布式事务的成功。
  • 本地事务失败,本地事务自动回滚,中间件负责其他(ServiceB,ServiceC的回滚)。
  • 如果分布式事务失败,发起方的本地事务不能自动回滚,则需要提供回调接口,由中间件帮助回滚。

异步RPC在全局事务中的定位。

异步请求如果在事务结束前,阻塞返回了,那么视为同步请求,加入了全局事务。
否则异步请求不在全局事务内

分布式事务解决方案(Saga)

事务处理流程

成功流程

分布式事务解决方案(Saga)

开启分布式事务失败,直接中止

分布式事务解决方案(Saga)

注册子事务失败,整体失败

分布式事务解决方案(Saga)

RPC失败,中间件回滚

分布式事务解决方案(Saga)

本地事务失败,中间件回滚

分布式事务解决方案(Saga)

回调中间件失败,检查事务状态

分布式事务解决方案(Saga)

事务发起方内部处理超时

开启分布式事务时,需指定本次事务超时时间。

记录本地事件表时,判断是否达到超时时间,如果达到,则拒绝提交,回滚本地事务。

中间件执行对事务参与方的回滚操作。

分布式事务解决方案(Saga)

Saga 服务设计的最佳实践

允许空补偿

  • 空补偿:原服务未执行,补偿服务执行了
  • 出现原因:
    • 原服务 超时(丢包)
    • Saga 事务触发 回滚
    • 未收到 原服务请求,先收到 补偿请求

所以服务设计时需要允许空补偿, 即没有找到要补偿的业务主键时返回补偿成功并将原业务主键记录下来

防悬挂控制

  • 悬挂:补偿服务 比 原服务 先执行
  • 出现原因:
    • 原服务 超时(拥堵)
    • Saga 事务回滚,触发 回滚
    • 拥堵的 原服务 到达

所以要检查当前业务主键是否已经在空补偿记录下来的业务主键中存在,如果存在则要拒绝服务的执行

幂等控制

  • 原服务与补偿服务都需要保证幂等性, 由于网络可能超时, 可以设置重试策略,重试发生时要通过幂等控制避免业务数据重复更新

缺乏隔离性的应对

  • 由于 Saga 事务不保证隔离性, 在极端情况下可能由于脏写无法完成回滚操作, 比如举一个极端的例子, 分布式事务内先给用户A充值, 然后给用户B扣减余额, 如果在给A用户充值成功, 在事务提交以前, A用户把余额消费掉了, 如果事务发生回滚, 这时则没有办法进行补偿了。这就是缺乏隔离性造成的典型的问题, 实践中一般的应对方法是:
    • 业务流程设计时遵循“宁可长款, 不可短款”的原则, 长款意思是客户少了钱机构多了钱, 以机构信誉可以给客户退款, 反之则是短款, 少的钱可能追不回来了。所以在业务流程设计上一定是先扣款。
    • 有些业务场景可以允许让业务最终成功, 在回滚不了的情况下可以继续重试完成后面的流程, 所以除了提供“回滚”能力还需要提供“向前”恢复上下文继续执行的能力, 让业务最终执行成功, 达到最终一致性的目的。
本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!