本文转载自微信公众号《潜行》,作者cscw。转载本文请联系SneakUp公众号。前言上一篇架构:分布式理论CAP、BASE[1],我们了解了分布式存在的问题和一般的解决方案理论,但是具体的实现协议或解决方案是什么?分布式共识分布式共识算法paoxs,Raft,zab分布式事务一致性分布式事务一致性实现方案(XA模式和AT模式)两阶段提交和三阶段提交灵活事务TCCAT模式事件通知1分布式一致性什么是分布式一致性?DistributionConsistency其实更偏向于解决多个服务之间数据副本状态的一致性,它不同于关系型数据库的一致性(数据约束)2分布式一致性算法paoxs算法Paxos算法是基于消息传递的,具有高度容错共识算法是目前公认的解决分布式一致性问题最有效的算法之一。Paxos算法通俗理解假设有十个人去旅行,目的地是成都和拉萨。为了统一目的地,简单的方法可以是开个微信群,大家投票,按照少数服从多数的原则。但是在Paxos算法中,我们觉得微信平台不靠谱,挂了怎么办?Paxos的原理是容错性一定要强,所以paxos用短信找另外三个人做中介(也可以十个人选,不限于三个中介),十个人给他们发短信,中介不需要相互通信。可以和一个人交流。大家疯狂发短信给中介,希望得到沟通权《沟通阶段》:如果一半的中介有沟通权。提议者将向这些中介提出他们想要的旅游目的地(如成都)。收到的结果分为三种;A:一半以上的中介同意拿东西去成都;B:至少有一个中介决定了旅游目的地(不一定是成都,也可以是其他提议者和中介约定的拉萨),然后先查旅游目的地是否超过半数。如果没有,下次你会选择最近的旅游目的地。C:失去通讯权,再继续发短信。.....Paxos的一致性是解决冗余副本的一致性,这和关系型数据库中ACID的一致性是不一样的。Raft算法因为Paxos而难以理解和实现。于是就有了新的共识算法。Raft有3个角色Leader:处理所有客户端交互,日志复制等,同时只有一个有效的LeaderFollower:类似voters,完全被动CandidateCandidate:任何server一开始都可以被选为新的leader选举阶段的都是Follwer,他们有一个内置的倒计时,当倒计时结束后,他们成为Candidates,并向其他follower发送请求让他们选举自己。此时存在三种状态A:超过一半的follower跟随成为新的leaderB:有竞争者,并且有超过一半的follower放弃竞选,成为它的followerC:有竞争者,每个人都平分秋色。候选人将在下一个选举周期内再次发起选举。这时,还有一个内置的倒数计时器。谁先完成倒计时,谁就成为leader,先夺取一半的follower(注意:上一轮成为其他follower的follower不能连任)Phase1:Leaderleader已经选出,client发送请求到添加一条日志,例如日志是“hello”2:Leader要求Follower按照他的指示,将这条新的日志内容追加到各自的日志中3:大多数follower服务器将日志写入磁盘文件后,确认添加成功,并发出CommittedOk4:在下一次心跳中,如果在此过程中出现网络分区或网络通信故障,Leader会通知所有follower更新committed项目。这使得Leader无法访问大部分Follwer,Follwer重新选举新的Leader对外提供服务。恢复网络时,旧领导者成为新领导者的追随者,拥有大多数追随者。失败时commitrollbackzab算法ZXID协议事务号Zxid设计,Zxid是一个64位的数字,低32位是一个简单的单调递增计数器,对于客户端的每一次事务请求,计数器都加1,高32位bits表示Leadercycle的epoch数。每选举出一个新的Leader服务器,它就会从Leader服务器中取出其本地日志中最大的事务ZXID,从中读取epoch值,然后加1,从而作为一个新的epoch。低32位计数器从0重新开始计数。崩溃恢复模式(选举)当集群初始化或Leader失去连接时,节点(任意节点)发起领导者选举,然后集群中的其他节点将投票给发起选举的节点。节点B判断如果确定A可以成为leader,则节点B将投票给节点A,判断依据为:electionepoch(A)>electionepoch(B)||zxid(A)>zxid(B)||同步化(A)>同步化(B)。并更新自己的投票给B投票。sid是服务ID。人为配置的消息广播方式Leader将客户端的请求转化为Proposal(提案)。Leader为每个Follower准备一个FIFO队列,并将Proposal发送到队列中。Leader如果收到超过半数的follower的ACK反馈,则leader向所有follower发送commit详情。leader收到客户端请求后,会将请求封装成一个事务,并为事务分配一个全局递增的唯一ID,称为TransactionID(ZXID),ZAB协议需要保证事务的顺序,所以每个事务都要排序根据ZXID再进行处理。Leader和Follower之间也有一个消息队列来解耦它们之间的耦合。同步阻塞zookeeper集群为了保证所有进程能够有序执行,只有Leader服务器可以接受写请求。Follower服务器即使收到客户端的请求,也会转发给Leader服务器处理。3分布式事务一致性对于分布式一致性和分布式事务一致性。我比较倾向于区分:A-分布式一致性是解决数据分布在多个服务中的状态一致性(多副本一致)B-分布式事务一致性,更类似于关系型数据库的一致性,是约束分布式中数据的关系服务(比如服务A中的数据a和服务B中的数据b的状态需要保持固定的映射关系)。分布式共识算法和分布式共识的区别共识算法是为了解决分布式共识4.分布式事务一致性实现(XA模式和AT模式)XA模式是一种预提交数据模式(预提交数据不能被其他事务使用access),如果失败,将回滚之前提交的数据。AT模式数据确认提交,但是有锁,其他事务无法访问数据。如果发生故障,则使用刷新操作修复数据。与XA模式相比,AT模式更适合解决分布式事务,减少阻塞等待时间。两阶段提交(强一致性)(XA模式)两阶段提交协议(Two-phaseCommit,即2PC)是一种常用的分布式事务解决方案,即将事务提交过程分为两个阶段进行处理:准备阶段和提交阶段处理流程阶段1:准备阶段协调者将交易内容发送给所有参与者,询问是否可以提交交易,等待所有参与者的回复。每个参与者执行一个事务操作,在事务日志中记录撤销和重做信息(但不提交事务)。如果参与者执行成功,则向协调者反馈yes,即可以提交;如果执行失败,则向协调器反馈no,即不能提交。参与者发送回滚消息;否则,发送提交消息。参与者根据协调者的指令执行提交或回滚操作,释放事务处理过程中使用的所有锁资源。2PC方案的缺点:性能问题:事务提交阶段所有参与者都处于同步阻塞状态,占用系统资源,容易造成性能问题。瓶颈可靠性问题:如果协调器存在单点故障问题,如果协调器出现故障,参与者将一直处于锁定状态数据一致性问题:在提交阶段,如果出现本地网络问题,部分事务参与者的提交消息,另一部分事务参与者没有收到提交消息,这将导致节点之间的数据不一致。三阶段提交(强一致性)(XA模式)三阶段提交协议是两阶段提交协议的改进版本。阶段提交的不同之处在于引入了超时机制。同时在coordinator和participant中都引入了超时机制处理流程stage1:canCommitCoordinator向participant发送commit请求,participant如果可以commit则返回yesresponse(参与者不执行事务操作),否则返回无响应:协调器向所有参与者发送包含事务内容的canCommit请求,询问是否可以提交事务,并等待所有参与者回复。参与者收到canCommit请求后,如果参与者认为可以进行事务操作,则反馈yes,进入准备状态,否则反馈no阶段2:preCommit协调器决定是否可以进行基于事务的preCommit操作根据第一阶段canCommit参与者的回应。根据响应,有两种可能的“case1”:第1阶段所有参与者反馈yes,参与者pre-executiontransactioncoordinator向所有参与者发送preCommit请求,进入准备阶段。参与者收到preCommit请求后,执行Transaction操作,在事务日志中记录undo和redo信息(但不提交事务)每个参与者向协调器反馈ack响应或不响应,等待最终命令”case2”:第一阶段任何参与者反馈否,“或者等待协调器超时,无法收到所有参与者的反馈,即中断交易”协调器向所有参与者发送中止请求“无论是否接收到协调器的中止请求,或者在等待协调器请求时发生超时,所有参与者都会中断事务”阶段3:doCommit该阶段进行真正的事务提交,分为以下三个工作状态下,向所有参与者发送doCommit请求,参与者收到doCommit请求后,正式执行事务提交和释放整个事务期间占用的资源。每个参与者都会将ack完成的消息反馈给协调器,协调器收到所有参与者的ack消息后,完成交易提交。“Case2”:在阶段2中,任何参与者反馈no,或者协调器等待超时后无法收到所有参与者的反馈,即如果协调器处于工作状态,则事务被中断,并向其发送中止请求所有参与者。参与者利用第一阶段的undo信息进行回滚操作,释放整个事务占用的资源。各参与者向协调器反馈ack完成消息,协调器收到所有参与者反馈的ack消息后,交易中断完成。“案例3”:协调者和参与者之间的网络有问题。会继续执行事务commit”的优缺点优点:第二阶段,协调者或参与者等待超时后会中断事务。优点:第三阶段,避免了协调者的单点问题。当出现问题时,参与者会继续提交交易(这也是一个缺点)。缺点:数据不一致的问题依然存在。第三阶段,如果协调者请求中断事务,协调者无法与参与者正常通信,参与者将继续提交事务,导致数据不一致灵活事务TCC(XA模式在服务层实现)Try阶段:需要检查储备资源在扣钱的场景下,Try中要做的事情就是检查账户的可用余额是否足够,然后冻结账户中的资金。Try方法执行后,虽然账户余额还是100,但是其中的30元已经被冻结,不能被其他交易使用了,账户A的余额变成了70元。Cancel阶段:如果回滚,需要在Cancel方法中释放Try第一阶段冻结的30元,使账户余额恢复到初始状态,100元可以全部使用AT模式(阿里分布式框架seata)第一阶段:提交第一阶段,Seata会拦截“业务SQL”,首先解析SQL语义,在“业务SQL”中找到需要更新的业务数据,保存为“before”业务数据更新之前。image”,然后执行“业务SQL”更新业务数据,业务数据更新后保存为“afterimage”,最后生成行锁。以上所有操作都在一个数据库事务中完成,保证了第一阶段操作的原子性。第二阶段提交或回滚。删除第一阶段保存的快照数据和行锁,完成数据清洗。如果是第二阶段的回滚,Seata需要回滚第一阶段已经执行过的“业务SQL”。恢复业务数据回滚的方式是使用“前映像”恢复业务数据;但在恢复之前,必须先验证脏写,对比“数据库当前业务数据”和“后映像”。如果两个数据完全一致,说明没有脏写,可以恢复业务数据。如果不一致,说明有脏写。如果有脏写,需要人工处理。事件通知(事务消息)同步通知者的惯性思维会考虑同步调用,是一种简单易实现的方案。但与第三方系统相比,不可靠,内部处理超时,网络断开,容易出现意外。而等待接口返回是一个阻塞过程,影响系统性能。与同步通知相比,异步回调通知多了一个异步回调处理接口。因此可以避免超时处理,超时返回的问题。考虑到回调时接口报错,需要发起重试回调,所以需要增加重试机制。消息队列消息队列可以解耦服务,解决错误重试的问题,因为接口调用时会出现错误或者重复调用,需要保证接口的幂等性。普通消息处理中的一致性问题:发送方业务逻辑处理成功 -> MQ存储消息成功 -> 但MQ处理超时 -> ACK确认failure?-> 导致发送方本地事务回滚,但实际MQ处理成功。如果有处理返回结果,也可以通过消息队列返回事务状态表+基于本地消息最终一致性方案的消息队列方案的核心做法是在进行业务操作时,记录一个消息数据到DB,并且消息数据和业务数据的记录必须在同一个事务中完成。消息数据发布后,可以使用定时任务去DB轮转状态为待发送的消息,然后将消息投递给MQ。在此过程中,可能存在消息传递失败的可能性。此时使用重试机制来保证在成功收到MQACK确认后消息状态被更新或清除。还要保证接口的幂等性。文中错误参考文章之一:分布式理论:Paxos算法通俗理解[2]分布式事务一致性解决方案[3]2PC与3PC[4]还是不懂“分布式事务”?这篇文章给你解释清楚![5]分布式事务-消息最终一致性方案[6]分布式事务?不,最终一致性[7]分布式事务的4种模式[8]分布式事务Seata(2)理解什么是AT、TCC、Saga[9]参考[1]架构:分布式理论CAP、BASE:https://juejin.cn/post/6948809101392478245[2]分布式理论一:Paxos算法通俗理解:https://www.cnblogs.com/esingchan/p/3917718.html[3]分布式事务一致性解决方案:https://www.cnblogs.com/williamjie/p/11200885.html[4]2PC和3PC:https://blog.csdn.net/skyie53101517/article/details/80741868[5]还是不懂“分布式事务”?这篇文章给大家解释清楚!:https://www.cnblogs.com/zjfjava/p/10425335.html[6]分布式事务-消息最终一致性方案:https://www.jianshu.com/p/04bad986a4a2[7]分布式事务?没有,最终一致性:https://zhuanlan.zhihu.com/p/25933039[8]分布式事务的四种模式:https://zhuanlan.zhihu.com/p/141645172[9]分布式事务Seata(二)理解什么是AT、TCC、Saga:https://www.jianshu.com/p/f2caa8737b7b
