分布式事务解决方案-SAGA(简单又实用)

「这是我参与11月更文挑战的第15天,活动详情查看:2021最后一次更文挑战

简介

SAGA事务模式的历史十分悠久,比分布式事务的概念提出还要更早。SAGA的意思是“长篇故事、长篇记叙、一长串事件”,它起源于1987年普林斯顿大学的赫克托 · 加西亚 · 莫利纳(Hector Garcia Molina)和肯尼斯 · 麦克米伦(Kenneth Salem)在ACM发表的一篇论文《SAGAS》。

文中提出了一种如何提升“长时间事务”(Long Lived Transaction)运作效率的方法,大致思路是把一个大事务分解为可以交错运行的一系列子事务的集合。原本提出SAGA的目的,是为了避免大事务长时间锁定数据库的资源,后来才逐渐发展成将一个分布式环境中的大事务,分解为一系列本地事务的设计模式。

Saga 是一种补偿协议,在 Saga 模式下,分布式事务内有多个参与者,每一个参与者都是一个冲正补偿服务,需要用户根据业务场景实现其正向操作和逆向回滚操作。

事务组成

SAGA由两部分操作组成。

一部分是把大事务拆分成若干个小事务,将整个分布式事务T分解为n个子事务,我们命名为T1,T2,…,Ti,…,Tn。每个子事务都应该、或者能被看作是原子行为。如果分布式事务T能够正常提交,那么它对数据的影响(最终一致性)就应该与连续按顺序成功提交子事务Ti等价。

另一部分是为每一个子事务设计对应的补偿动作,我们命名为C1,C2,…,Ci,…,Cn。Ti与Ci必须满足以下条件:

  • Ti与Ci都具备幂等性;
  • Ti与Ci满足交换律,即不管是先执行Ti还是先执行Ci,效果都是一样的;
  • Ci必须能成功提交,即不考虑Ci本身提交失败被回滚的情况,如果出现就必须持续重试直至成功,或者要人工介入。

如果T1到Tn均成功提交,那么事务就可以顺利完成。否则,我们就要采取以下两种恢复策略之一:

  • 正向恢复(Forward Recovery): 如果Ti事务提交失败,则一直对Ti进行重试,直至成功为止(最大努力交付)。这种恢复方式不需要补偿,适用于事务最终都要成功的场景,比如在别人的银行账号中扣了款,就一定要给别人发货。正向恢复的执行模式为:T1,T2,…,Ti(失败),Ti(重试)…,Ti+1,…,Tn。
  • 反向恢复(Backward Recovery): 如果Ti事务提交失败,则一直执行Ci对Ti进行补偿,直至成功为止(最大努力交付)。这里要求Ci必须(在持续重试后)执行成功。反向恢复的执行模式为:T1,T2,…,Ti(失败),Ci(补偿),…,C2,C1。

实现方式

常用实现方式有两种:一个是集中式协调器的实现方式,一个基于事件的实现方式。

集中式实现

集中式的实现一般是通过一个Saga对象来追踪所有的Saga子任务的调用情况,根据调用情况来决定是否需要调用对应的补偿方面,协调器和调用方是在一个进程中的。集中式的Saga实现方式比较直观并且容易控制,问题是业务耦合程度会比较高。

例子:已订单流程为例,完成流程包含以下服务

  • Order Service:订单服务
  • Payment Service:支付服务
  • Stock Service:库存服务
  • Delivery Service:物流服务

集中式的实现如下:

参考:blog.couchbase.com/saga-patter…

image.png

  1. 订单服务创建一笔新订单,将订单状态设置为"待处理",然后让Order Saga Orchestrator(OSO)开启创建订单事务。(Order Service和Order Saga Orchestrator 大多数情况可以是一个模式)
  2. OSO发送一个Execute Payment给支付服务,支付服务完成扣款并回复Payment Executed消息。
  3. OSO发送一个Prepare Order给库存服务,库存服务完成库存扣减和备货,并回复Order Prepared消息。
  4. OSO发送一个Deliver Order给物流服务,物流服务完成配送,并回复Order Delivered消息。
  5. OSO向订单服务发送"订单结束命令"给订单服务,订单服务将订单状态设置为"完成"。

回滚示例图:

image.png

我们现实中使用的就是这种案例,只是稍微有些变化,简单讲下,也是创建订单业务,创建订单有主要三步操作:
1.扣减库存 2.扣减优惠券 3.落库 以上三步会多线程rpc调用相关服务,如果全部成功,订单正常创建成功,如果有一步失败或者超时,就发失败回滚消息(失败回滚消息要保证发送成功),各服务保证能正常回滚。

基于事件的实现方式

在基于事件的方式中,第一个服务执行完本地事务之后,会产生一个事件。其它服务会监听这个事件,触发该服务本地事务的执行,并产生新的事件。

例子参考:blog.couchbase.com/saga-patter…

例子:同样订单流程,每个服务执行都会产生一个事件,下一个服务监听,下一个服务执行完后,再产生新的事件

正向流程: image.png 逆向流程:库存失败,需要回滚支付

image.png

Saga模式的优劣势

优势

  • 一阶段提交本地数据库事务,无锁,高性能;
  • 参与者可以采用事务驱动异步执行,高吞吐;
  • 补偿服务即正向服务的“反向”,易于理解,易于实现;

劣势

Saga 模式由于一阶段已经提交本地数据库事务,且没有进行“预留”动作,所以不能保证隔离性。如果两个Saga事务同时操作同一资源就会遇到我们操作多线程临界资源的的情况,就会产生更新丢失,脏数据读取等问题。

阿里开源组件Seata Saga 服务设计经验

Saga 服务设计 - 允许空补偿 image.png Saga 服务设计 - 防悬挂控制

image.png

Saga 服务设计 - 幂等控制

image.png

Saga 设计 - 自定义事务恢复策略 image.png

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

猜你喜欢

转载自juejin.im/post/7031155275981668360