隐藏

.net core分布式事务落地(1)-分布式概念及解决方案

发布:2023/8/3 11:15:04作者:管理员 来源:本站 浏览次数:611


目录


1. 为什么需要分布式


1.1 传统架构带来的局限性


1.2 系统复杂带来的维护性


2. 分布式带来的问题-CAP分布式事务


2.1 Consistency(数据一致性)


2.2  Availability(系统可用性)


2.3  Partition tolerance(分区容错性)


3. 分布式事务的解决方案


3.1 强一致性


3.1.1 介绍


3.1.2 方案


3.1.2.1 2PC


3.1.2.1.1 简介


3.1.2.1.2 2PC示例


3.1.2.1.3 优点


3.1.2.1.4 缺点


3.1.2.2 3PC


3.1.2.2.1 简介


3.1.2.2.2 3PC示例


3.1.2.2.3 优点


3.1.2.2.4 缺点


3.2 弱一致性


3.2.1 介绍


3.2.2 方案


3.2.2.1 TCC


3.2.2.1.1 简介


3.2.2.1.2 TCC示例


3.2.2.1.3 优点


3.2.2.1.4 缺点


3.2.2.2 Saga


3.2.2.2.1 简介


3.2.2.2.2 Sage示例


3.2.2.2.3 优点


3.2.2.2.4 缺点


3.3 最终一致性


3.3.1 介绍


3.3.2 方案:本地消息表+队列


3.3.3 优点


3.3.4 缺点



1.  为什么需要分布式?


为什么系统需要分布式,我认为可以从两个个方面出发:

1.1传统架构带来的局限性


传统系统架构是将整个系统做成一个服务来对外提供服务,所有的业务步骤都在一个服务内完成,将这个服务部署到一台计算机上。但是我们都知道单台计算机的性能是有限的,从而导致一台计算机能处理的请求是局限的,最终的结果是处理不了业务从而直接导致经济损失。


举个实际生活中的例子,比如:开发一个软件,我们将它的步骤分为:收集需求-需求分析-系统设计-系统开发-测试-上线-运维等。规定在三个月内做完,如果这些事都交给一个人来干,三个月肯定是干不完的。所以需要将步骤拆分出去,增加人手,每个步骤由专门的人去干,整个开发以迭代式的方  式进行。其实将整个步骤拆分开,分别执行,这个就好比我们将整个系统进行拆分,拆分成一个个子系统,每个子系统运行在不同的计算机上。组合起来共同完成任务。这就是分布式,也是为什么需要分布式的原因之一。

1.2系统复杂带来的维护性


传统系统架构因为将整个业务都写在一个服务内,所有的代码都堆叠在一起,随着业务的复杂性越来越大,导致系统越来越难维护。而分布式,将业务拆分开来,每个子系统负责自己的业务,独立维护。从而大大的提高了系统的可维护性。



2.  分布式带来的问题-CAP


因整个业务系统拆分成一个一个子系统,分别运行在不同的计算机上,从而带来了三个问题

2.1 Consistency(数据一致性)


因每个业务系统分部在不同计算机,中间可能因为各种原因导致数据不一致。例如:电商系统中将订单和支付分为两个子系统,现有这么一个操作,用户支付了某个订单,该操作会调用支付系统和订单系统进行支付和更新订单状态,那么如果在支付成功之后更新订单失败,那么就导致数据不一致。

2.2 Availability(系统可用性)


系统可用性,因为有多个子系统,一旦某个子系统不可用,将会导致整个系统不可用。例如:电商系统中的订单和支付子系统,当支付子系统宕机或者说请求堵塞了,将会导致后续请求及业务无法处理,从而带来经济损失,导致整个系统无法使用。

2.3 Partition tolerance(分区容错性)


一个分布式系统里面,节点组成的网络本来应该是连通的。然而可能因为一些故障,使得有些节点之间不连通了,整个网络就分成了几块区域。数据就散布在了这些不连通的区域中。这就叫分区。


当你一个数据项只在一个节点中保存,那么分区出现后,和这个节点不连通的部分就访问不到这个数据了。这是分区就是无法容忍的。


提高分区容忍性的办法就是一个数据项复制到多个节点上,那么出现分区之后,这一数据项就可能分布到各个区里。容忍性就提高了。


然而,要把数据复制到多个节点,就会带来一致性的问题,就是多个节点上面的数据可能是不一致的。要保证一致,每次写操作就都要等待全部节点写成功,而这等待又会带来可用性的问题。


总的来说就是,数据存在的节点越多,分区容忍性越高,但要复制更新的数据就越多,一致性就越难保证。为了保证一致性,更新所有节点数据所需要的时间就越长,可用性就会降低。

3.  CAP的解决方案


CAP三个问题中,P(分区容错)是固定存在的,主要是在于C(数据一致性)和P(系统可用性)之间做出取舍。针对二者,主要有三个方案。

3.1强一致性

3.1.1介绍


强一致性的本质是对资源进行锁定,然后在进行相关操作,从而保证在任何时刻所有的用户或者进程查询到的都是最近一次成功更新的数据。最具代表性的方案有:2PC、3PC。

3.1.2方案

3.1.2.1 2PC

3.1.2.1.1 简介


2PC是解决分布式事务的一个协议,当一个事务跨越多个节点时,为了保持事务的ACID特性,需要引入一个coordinator,即协调者作为的组件来统一掌控所有节点(称作参与者)的操作结果并最终指示这些节点是否要把操作结果进行真正的提交或回滚。


2PC 本质是: 参与者将操作成败通知协调者,再由协调者根据所有参与者的反馈决定各参与者是否要提交操作还是回滚操作。



3.1.2.1.2 2PC示例:


还是拿上面的操作来说,电商系统中,有两个子系统:订单和支付。现有一用户支付某一订单,支付成功之后需修改订单状态。


在2PC中,需要经过两个步骤,第一,准备提交,锁定资源。第二,提交事务,确认提交。整个流程是,由协调者向支付系统跟订单系统发送准备提交的请求,当两个子系统都返回准备完成时,这个时候资源是被锁住的,也就是咱们的数据或者表被锁定了。其次,当两个都返回成功之后,协调者在将事务真正提交,然后进行一个事务提交完成确认的过程,如果都返回成功,那么整个事务就成功了。


以下为事务成功示意图:










图:2PC成功事务示意图


如果说在事务中间有一步出现错误或者说提交失败,例如,当支付成功,更新订单提交事务的时候,确认提交失败,那么协调者将会通知所有事务回滚。以下为示例图:








图:提交事务失败示意图


当更新订单操作,确认提交事务失败,那么协调者将会给支付系统及订单系统发送回滚事务操作。最终返回支付失败。

3.1.2.1.3 优点


                      1. 数据一致性很高,用户或者进程查询到的都是最近一次成功更新的数据

3.1.2.1.4 缺点


1. 当某个协调者本身在提交完事务之后宕机,那么其他参与者将会处于一个停滞状态,会一直锁住数据库资源,从而导致系统卡住。


2. 因为2PC是处理事务是连锁的,进行一个支付订单操作,将锁住两个子系统,从而导致被锁住的时间,这两个系统是无法使用的,整个系统能承载的用户量就比较小,也就是吞吐量降低。

3.1.2.2 3PC

3.1.2.2.1 简介


因2PC存在巨大缺陷,协调者宕机,将导致整个系统被锁住,于是便有了3PC的出现,3PC其本质是基于2PC,在2PC的基础上引入了两个,分别是:


       超时机制。在协调者和参与者中都引入超时机制。一旦协调者或者参与者未能得到恢复,超过一定时间将认为事务失败,则进行回滚。

       新增事务预备阶段。在准备步骤和提交事务步骤中插入了一个PreCommit步骤,用于确认并保证提交事务前各参与节点状态一致。


3.1.2.2.2 3PC示例


那么上面的例子,用户支付某个订单,就变成了这样:








预提交事务,并不会真正执行事务,只是用于确认参与者能否执行事务,确认参与者的状态等。


超时机制,当3个环节,只要有一个环节超时,比如协调者宕机,那么其余参与者将自动回滚事务。在例如订单系统宕机,那么协调者将发送事务回滚。



3.1.2.2.3 优点


1. 数据一致性很高,用户或者进程查询到的都是最近一次成功更新的数据

3.1.2.2.4 缺点


                    1. 事务会锁定数据库资源,系统可用性较低,能承载的请求数有限。

3.2弱一致性

3.2.1介绍


因为强一致性的方案会导致数据被锁住,整个系统吞吐量降低。于是就有了弱一致性,而弱一致性的本质是在程序级别执行事务,主要是保存了数据的变化版本,一旦出现执行出现错误,那么将执行回滚,将数据回滚到初始状态。弱一致性最具代表性的方案有:TCC、Saga。

3.2.2方案

3.2.2.1 TCC

3.2.2.1.1 简介


TCC全称(Try Confirm Cancel),是分布式事务弱一致性的一种解决方案,本质上是在业务服务层实现事务,对于每个业务的子系统都需要需要实现三个方法,分别是:Try尝试执行业务、 Confirm确认执行业务、 Cancel 取消执行业务。


           Try的作用及目标:


               完成所有业务检查(一致性)

               预留必须业务资源(准隔离性)    


2. Confirm的作用及目标:


               真正执行业务

               不作任何业务检查

               只使用Try阶段预留的业务资源

               Confirm操作要满足幂等性


3. Cancel的作用及目标:


               释放Try阶段预留的业务资源

               Cancel操作要满足幂等性


3.2.2.1.2 TCC示例


             还是以电商系统中的订单及支付子系统为例,对于同样的操作,用户支付某个订单,在TCC中是如何实现:








图:TCC用户支付某订单示意图


            第一步,先分别调用订单系统及支付系统的try接口,如果返回成功,那么则进行第二步操作。


            第二步,分别调用支付及订单系统的confirm接口,如果成功,则整个业务成功。


            如果中间某一个环节出现错误,例如调用支付的try成功了,调用订单的try失败。那么将调用其它系统的cancel接口进行回滚,以下为示意图:








图:TCC某节点调用失败

3.2.2.1.3 优点:


           相对于2PC、3PC来说,因为事务在业务服务层,事务不会锁定数据库资源,能提高系统可用性。

           没有单独的准备(Prepare)阶段, Try操作兼备资源操作与准备能力 。

           因为是在业务服务层执行事务,可以灵活选择业务资源的锁定粒度。


3.2.2.1.4 缺点:


                      1. 提升了开发成本,每个业务需要实现三个接口。

3.2.2.2 Saga

3.2.2.1 简介


Saga是一种分布式事务的补偿协议,业务流程中每个参与者都提交本地事务,当出现某一个参与者失败则补偿前面已经成功的参与者。其正向服务(具体业务)及补偿服务(回滚,撤销)都由业务开发实现。与TCC相似。

3.2.2.2 Saga示例


我们继续上面的业务场景,还是订单系统和支付系统,依旧是相同的操作,用户支付某订单。在Saga中,需要实现一个补偿事务的方法,主要用于事务失败之后执行,整个业务流程为:第一,分别向订单系统及支付系统发送请求,支付成功之后更新订单,二者系统本地分别启动事务。第二,如果执行成功,那么整个业务操作成功,如果失败,那么将执行补偿事务的方法,进行事务回滚。以下为示意图:








图:Saga成功示意图








图:Saga失败示意图

3.2.2.3 优点


           相对于2PC、3PC来说,因为事务在业务服务层,事务不会锁定数据库资源,能提高系统可用性。

           没有单独的准备(Prepare)阶段,性能提升。

           因为是在业务服务层执行事务,可以灵活选择业务资源的锁定粒度。


3.2.2.4 缺点


                        1. 提升了开发成本,每个业务需要实现两个接口。

3.3最终一致性

3.3.1 简介


因强一致性和弱一致性,或多或少都会锁定资源,对于高并发场景,并不太适合。于是便有了数据最终一致性,数据最终一致性的本质是,数据暂时可以不一致,但是最终数据是一致的。例如:银行转账,可以先扣除一方的钱,但是另一方可以暂时没收到,但是最终还是需要收到,转账成功的。其具体实现是,如果失败,则不断地重试,如果重试一定次数,那么进行人工操作,完成业务。最终一致性的方案用的最多的还是使用本地消息表,基于EventBus模式去实现。

3.3.2 方案:本地消息表+队列


我们还是以电商系统中的订单和支付系统为例子,依旧是用户支付某订单,那么在最终一致性下,是这样的:第一,业务端发起一个支付。第二支付系统,支付某个订单,将支付完成的消息放入本地消息表中,一个线程读取本地信息放入队列中,第三订单系统,读取队列信息,获取支付成功的某个订单信息,存放本地消息表,然后启动本地事务进行订单的更新。如果中间有失败,那么先会有一个重试机制,如果重试失败,最终进行人工处理。以下为示意图




图:最终一致性示意图




如果中间有环节失败,比如订单同步消息失败,那么也是走人工处理。一般来说,走人工处理的可能性几乎不存在,最终一致性的目的是,本身用户就要干这个事情,那么这个事情一定是要成功的。

3.3.3 优点


                 1. 不会锁住资源,极大的提高了系统可用性


                 2. 利用第三方队列或者其它手段,进行系统之间解耦。


                 3. 没有任何准备阶段,只需要提交数据就行

3.3.4 缺点


                 1. 数据短时间内不一致