欢迎您访问365答案网,请分享给你的朋友!
生活常识 学习资料

分布式系统下数据一致性方案

时间:2023-06-15
理论知识 CAP C: 一致性;数据如果存在多个副本,这些副本的数据应该是一致的。A: 可用性;注意没有限定成HA(高可用性),理解错了可能会有些误导。P: 分区容错性;当系统发生了网络分区的时候,仍然能正常提供服务。

一般认为分布式系统CAP无法都保证,以下是我对这个结论的推演过程的一些想法:

这个理论 的一个重要前提是网络可能会无限期挂掉只有一个节点也能保证A,因为节点挂了在这个理论假设中是可以短时间恢复的,只要短时间内能恢复,就不算不满足A。不能三个都保证不是说三个只能选两个,而是说不能同时在比较高的指标上维持这三个特性;例如CAP各有100分,不是C,P选了100,A就必须是0,一般情况下就是C 80, P 80, A 50或者A 80,P 80, C 50这种折中。C和A实际上无法在分布式系统中同时保证,只有单机的情况下才存在同时具备可用性和一致性,因为只要在分布式环境中,就有可能发生分区,发生分区之后要保证一致性只能停写,停写就不能保证A了(写操作不能执行)。因此,分布式系统中一般会选择CP 或者AP,根据不同的业务场景做不同的选择。选择CP就是分区之后停写,抓紧恢复网络,降低A;选择AP那就是分区了之后各自提供服务,先保证服务基本可用,抓紧恢复网络,恢复了之后再合并数据修到一致。 base

base 可以理解成分布式系统的事务ACID;ACID是单机数据库在没有网络故障假设下的强一致模型;在分布式环境下很难做到,可以使用base代替:

base Available: 基本可用,在系统发生故障下,保证核心功能可用即可,例如在大促时用户可能会降级到默认页。Soft state: 软状态,允许数据存在中间状态,也就是短暂的不一致。Eventual Consistency: 最终一致性 两阶段提交

两阶段提交(2PC)是一个分布式事务算法,其定义大致如下:
两阶段提交定义了事务中的两种角色:

参与者:执行本地事务的进程。协调者:监控各个参与者的本地事务执行状态,决定是否要提交全局事务或者回滚全局事务。
两阶段提交将事务的执行划分成了两个阶段:prepareCommit: 协调者给每个参与者发送事务执行的内容,每个参与者执行本地事务,可以理解成数据库记录redolog, undolog,但是没提交;如果执行完没有问题,就向协调者返回yes,否则返回nodoCommit: 这一阶段根据前一阶段各个参与者的返回执行不同的操作;

如果都按时返回了yes,那就通知所有参与者执行commit如果有一些参与者返回no或者超时了,就通知所有参与者执行rollback

这个过程实际上是有一些问题的:

参与者没有超时机制,如果第二阶段进行中协调者挂了,参与者就会一直处于资源锁定的状态,又提交不了。第二阶段发出提交或回滚的结论之后,这个消息不一定会被正确收到,还是有可能不一致 三阶段提交

三阶段提交基于两阶段提交改进了一些问题:

三阶段提交同时给协调者和参与者加上了超时机制三阶段将两阶段提交的第一阶段拆开成了两个步骤

三阶段提交的大致流程如下:

canCommit: 协调者询问每个参与者是否能执行事务(事务所需的资源是否就位),这个过程中参与者不直接执行事务,如果可以执行,参与者返回yes,否则参与者返回noprepareCommit: 协调者收到了第一阶段所有参与者返回的yes之后就会发送prepareCommit 消息给参与者,告诉参与者开始执行事务操作,写数据,记log;如果没有按时收到全部的yes,那么就会向参与者发送abort。如果参与者超时没收到preCommit请求,那么会中断事务并回滚。doCommit: 当协调者收到了所有参与者对prepareCommit消息回复的yes之后就会发送doCommit的请求给所有参与者,反之会发送abort请求告诉参与者中断事务;需要注意的是,在这个阶段如果参与者超时没收到第三阶段的消息,是会默认提交事务的,有可能导致数据不一致,不过理论上需要回滚的概率比较小,因为都经过两轮投票了。 实现 XA

XA是对两阶段提交算法的一个具体实现,是一个协议,许多数据库都提供了XA事务中RM(资源管理器)角色的实现;实现XA事务需要数据库实现如下操作:

1 实现开启全局事务的接口;例如mysql 的xa start txid2 实现将本地事务设为prepared的接口;例如mysql的xa prepare txid3 提交本地事务;例如mysql的xa commit txid4 回滚本地事务;例如mysql的xa rollback txid

除此之外,mysql还实现了这些命令:

xa end txid;标记本地事务中的sql操作已经完成,可以执行xa prepare txid进入待提交状态了xa recover;显示本地处于prepare状态的所有xa事务。

在实际执行过程中,数据库作为RM只感知本地事务,应用程序作为协调者掌控全局。

这篇文章讲得很好;

TCC

XA实现的分布式事务对资源的锁定时间长,锁定粒度大,效率低;因此提出了TCC模型来实现分布式事务,TCC模型要求每个事务参与者实现:Try, /confirm/i, Cancel 这三个逻辑,在这三个逻辑中分别进行如下的操作:

Try:检查资源是否满足事务执行的条件,并锁定资源。/confirm/i:执行事务操作;Cancel:某一个分支事务失败时回滚以及释放资源。

以一个订单创建的流程为例,简单描述TCC模式的执行流程;假设一个订单创建的过程包含两个操作,库存服务扣减订单中的库存,订单服务创建订单:

主业务逻辑向订单和库存服务分别发送创建订单和扣减库存的try消息订单服务和库存服务检查自己的状态(例如库存数量够不够等),返回是否能执行事务操作

如果订单服务和库存服务对try消息都返回了成功,那么主业务逻辑对订单服务和库存服务发送/confirm/i消息如果其中一个返回了失败,那么主业务逻辑将发送cancel消息给订单服务和库存服务,释放掉占用的资源 订单服务和库存服务收到/confirm/i的消息之后就会开始执行真正的事务操作,并向主业务逻辑反馈执行的结果

如果所有微服务对/confirm/i的执行结果都是成功,那么主业务逻辑关闭事务,如果订单微服务或者库存微服务在执行/confirm/i的时候出错了,主业务逻辑在收到错误消息后会发送cancel消息到订单服务和库存服务,将整个事务置为失败的状态,回滚已经进行的操作以及释放资源。

当try阶段发生错误时,执行流程如下:

使用tcc的方式需要各个业务方对每个业务操作定义try, /confirm/i, cancel 操作,对业务方侵入比较大。

SAGA事务协议

SAGA事务协议的基本内容如下:

每个事务由若干个幂等的子事务 T1 T2 T3 ..、Tn每个子事务有一个回滚操作 C1 C2 C3 ..、Cn

SAGA事务有两种回滚模式:

前向回滚;适用于一定要成功的场景,当某个子事务执行出错了,重试直到其成功;后向回滚;适用于有可能出现业务错误的场景,此时按照执行的顺序反向执行回滚操作,例如T1 T2 执行成功了,T3执行失败了,这时执行C2, C1将T2 和T1 的修改回滚。

SAGA事务还有两种执行模式:

基于命令协调者的模式;有一个类似协调者的角色,告诉每个子事务处理者什么时间该做什么操作。基于事件编排的模式,类似DDD中事件驱动的做法,定义一个状态机,每个微服务去订阅事件,按照状态机中规定的事务流转规则来做操作,包括补偿。

SAGA事务不保证隔离性,所以有脏写的问题,比如账号A和账号B转钱,先给账号B充值了,然后扣A的余额,结果扣A的余额失败,需要回滚,但是B已经把钱花了,此时就没法回滚了,因此在这种业务场景下,可以先考虑扣A的钱。

详见这篇文章

本地消息表

另一种实现分布式数据一致性的方式是通过本地消息表来传递事务消息,此时没有一个居中调停的协调者,各个分布式事务的参与方通过数据库中的本地消息表中的消息来触发事务的执行,以上面的创建订单的流程为例,执行过程大致如下:

订单服务创建订单,并向消息表中写入记录,事务进度流转到库存服务负责的部分库存服务轮询消息表,发现有需要执行的事务操作,开始执行扣减库存的业务

如果扣减成功,库存服务会将消息状态切换为完成,意味着这次事务成功执行如果因为业务错误执行失败,库存服务会将消息状态切换为回滚中,其它参与了本次事务的微服务也会一直监听回滚中的消息,并回滚事务 消息队列

消息队列的实现方式和本地消息表类似,但是是通过mq来传递事务消息。本地消息表和消息队列的模式与DDD中的事件总线类似,通过定义一个事务操作的各种状态以及系统需要在各个状态下做的操作,来实现最终一致性。

Copyright © 2016-2020 www.365daan.com All Rights Reserved. 365答案网 版权所有 备案号:

部分内容来自互联网,版权归原作者所有,如有冒犯请联系我们,我们将在三个工作时内妥善处理。