分布式事务解决方案以及 .Net Core 下的实现(上)

数据一致性是构建业务系统需要考虑的重要问题 , 以往我们是依靠数据库来保证数据的一致性。但是在微服务架构以及分布式环境下实现数据一致性是一个很有挑战的的问题。最近在研究分布式事物,分布式的解决方案有很多解决方案,也让我在研究的同时也引发了很多思考。今天我想讲的是分布式事物解决方案是和saga有关。

原文地址:微服务场景下的数据一致性解决方案 incubator-servicecomb-saga地址:incubator-servicecomb-saga servicecomb-saga-csharp(servicecomb-saga netcore sdk)地址:servicecomb-saga-csharp

根据原文做一些解释性的地方 方便更加理解

单体应用的数据一致性

我就给大家讲一个国外经常用到的例子吧,就是假如有一家大型的企业,下属有航空公司、租车公司、和连锁酒店。这个大公司为客户提供一站式的旅游行程规划服务,这样客户只需要提供出行目的地, 这个大公司能帮助客户预订机票、租车、以及预订酒店。从业务的角度,我们必须保证上述三个服务的预订都完成才能满足一个成功的旅游行程,否则不能成行。

我们的单体应用要满足这个需求非常简单,只需将这个三个服务请求放到同一个数据库事务中,数据库会帮我们保证全部成功或者全部回滚。

这三个服务上线公司满意,客户也很满意

微服务场景下的数据一致性

随之时间的推移,这个大企业的行程规划服务非常成功,用户量剧增上百倍。企业的下属航空公司、租车公司、和连锁酒店也相继推出了更多服务以满足客户需求, 我们的应用和开发团队也因此日渐庞大。如今我们的单体应用已变得如此复杂,以至于没人了解整个应用是怎么运作的。更糟的是新功能的上线现在需要所有研发团队合作, 日夜奋战数周才能完成。看着市场占有率每况愈下,公司高层对研发部门越来越不满意。

经过数轮讨论,领导最终决定将庞大的单体应用一分为四:机票预订服务、租车服务、酒店预订服务、和支付服务。服务各自使用自己的数据库,并通过HTTP协议通信。 负责各服务的团队根据市场需求按照自己的开发节奏发版上线。如今我们面临新的挑战:如何保证最初三个服务的预订都完成才能满足一个成功的旅游行程, 否则不能成行的业务规则?现在服务有各自的边界,而且数据库选型也不尽相同,通过数据库保证数据一致性的方案已不可行。

Sagas

经过一段时间的查找,我发现了一篇论文,1987年Hector & Kenneth 发表论文 Sagas论文地址

Saga是一个长活事务(Long Live Transaction (LLT)),可被分解成可以交错运行的子事务集合。其中每个子事务都是一个保持数据库一致性的真实事务(LLT = T1 + T2 + T3 + ... + Tn)。每个本地事务Tx 有对应的补偿 Cx。

在大企业的业务场景下,一个行程规划的事务就是一个Saga,其中包含四个子事务:机票预订、租车、酒店预订、和支付。

根据上面提到的公式

当每个saga子事务 T1, T2, …, Tn 都有对应的补偿定义 C1, C2, …, Cn-1, 那么saga系统可以保证 [1]子事务序列 T1, T2, …, Tn得以完成 (最佳情况)或者序列
T1, T2, …, Tj, Cj, …,
C2, C1, 0 < j < n,
得以完成

换句话说,通过上述定义的事务/补偿,saga保证满足以下业务规则:

所有的预订都被执行成功,如果任何一个失败,都会被取消
如果最后一步付款失败,所有预订也将被取消,这些取消就是所谓的补偿。

Saga的恢复方式

原论文中描述了两种类型的Saga恢复方式:

向后恢复 补偿所有已完成的事务,如果任一子事务失败。向前恢复 重试失败的事务,假设每个子事务最终都会成功

显然,向前恢复没有必要提供补偿事务,如果你的业务中,子事务(最终)总会成功,或补偿事务难以定义或不可能,向前恢复更符合你的需求。

理论上补偿事务永不失败,然而,在分布式世界中,我们来想想极端的情况,无非就是往三种可能去考虑,成功,失败,超时(有可能成功,也有可能失败)。那么服务器可能会宕机,网络可能会失败,甚至数据中心也可能会停电。在这种情况下我们能做些什么? 最后的手段是提供回退措施,比如人工干预。

补充说明:ACID与SAGA

原子性(Atomicity):Saga只提供ACD保证,原子性(通过Saga协调器实现

一致性(Consistency):本地事务 + Saga log

隔离性(Isolation):Saga不保证

持久性(Durability):Saga log 提供

有很多朋友会说怎么不提供隔离性啊?

例子地址:地址

两个Saga事务同时操作一个资源会出现数据语义不一致的的情况

两个Saga事务同时操作一个订单 ,彼此操作会覆盖对方(更新丢失)

两个Saga事务同时访问扣款账号,无法看到退款 (脏读取问题)

在一个Saga事务内,数据被其他事务修改前后的读取值不一致(模糊读取问题)

面对以上问题我们应该如何应对隔离性问题呢?

下面给出对应的解决方案

隔离的本质是控制并发,防止并发事务操作相同资源而引起结果错乱

在应用层面加入逻辑锁的逻辑。

业务层面采用预先冻结资金的方式隔离此部分资金。

业务操作过程中通过及时读取当前状态的方式获取更新。

使用Saga的条件

Saga看起来很有希望满足我们的需求。所有长活事务都可以这样做吗?这里有一些限制:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpppjx.html