精华内容
下载资源
问答
  • 3PC的优点和缺点: 优点: 1.相比2PC,最大的优点就是降低了第一阶段的阻塞范围(第一阶段是不阻塞的...2.能够在单点故障后继续达成一致(2PC在提交阶段会出现此问题),而3PC会根据协调者的状态进行回滚或者提交 ...

    1.分布式理论:一致性协议:2PC

    1.1 什么是2PC
    
    2PC,即两阶段提交协议的简写,是整个事务过程分为两个阶段,准备阶段(Prepare phase)和提交阶段(Commit phase)
    
    两个阶段过程:
    1.准备阶段:事务管理器给每个参与者发送Prepare消息,每个数据库参与者在本地执行事务,并写本地的Undo/Redo日志,此时事务没有提交。
    Undo日志是记录修改前的数据,用户数据库回滚.
    Redo日志是记录修改后的数据,用于提交事务后写入数据文件
    
    2.提交阶段:如果事务管理器接收到了参与者的执行失败或者超时消息时,直接给每个参与者发送回滚(RollBack)消息,否则,发送提交(Commit)消息;
    参与者根据事务管理器的指令执行提交或者回滚操作。并且释放事务处理中所需的锁资源。
    注意:必须在最后阶段才释放锁资源
    
    2. 2PC执行流程
    成功执行事务事务提交流程 ,如下
    

    在这里插入图片描述

    阶段一:
    1.事务询问:协调者向所有参与者发送事务内容,询问是否可以执行事务提交操作。
    并开始等待各参与者的响应
    2.执行事务(写本地的Undo/Redo日志)
    3.各参与者向协调者反馈事务询问的响应
    
    总结:各个参与者进行投票是否让事务进行
    
    Tip:什么是Ack
    Ack确认字符,在数据通信中,接收站发给发送站的一种传输类控制字符。
    表示发来的数据已确认接收无误
    
    
    阶段二:
    1.发送提交请求:
    	协调者向所有参与者发出Commit请求
    2.事务提交
    	参与者收到Commit请求后,会正式执行事务提交操作,并在完成提交之后释放整个事务执行期间占用的事务资源
    3.反馈事务提交结果
    	参与者在完成事务提交之后,向协调者发送Ack信息
    4.完成事务
    	协调者接收到所有参与者反馈的Ack信息后,完成事务
    
    3.2PC的优点和缺点:
    1.优点:原理简单,实现简单
    2.缺点:同步阻塞,单点问题,数据不一样,过于保守
    
     - 同步阻塞
     	这是二阶段提交协议最大最明显的问题,在提交过程中,所有参与该事务操作的逻辑都处于阻塞状态,也就是所有参与者在等待其它参与者相应的过程中,无法进行其它操作。这种同步阻塞极大限制了分布式系统的性能
     - 单点问题
     	协调者在二阶段提交协议中十分重要,一旦出现故障,整个流程就无法运转,更重要的是,其它参与者都会处于一个锁定事务的状态中,而无法继续完成该事务
     - 数据不一致
     	假设当协调者在给所有参与者发送commit事务后,出现局部网络问题或者协调者在没有发送完所有commit请求的时候突然崩溃,导致最终只有部分参与者收到了Commit请求,这将导致严重的数据不一致问题
     - 过于保守
    	如果在二阶段提交的询问阶段中,参与者出现故障或者协调者没有获取到所有参与者的响应信息,这个时候协调者只能依靠自身的超时机制来判断是否需要中断事务。显然,这种策略过于保守。换句话说,二阶段提
    交协议没有设计较为完善的容错机制,任意一个节点失败都会导致整个事务的失败。
    
    1.什么是3PC
    3PC是2PC的改进版,将2PC的"提交事务请求"过程一分为二,共形成了由CanCommit,PreCommit和doCommit三个阶段组成的事务处理协议
    

    在这里插入图片描述

    阶段一:CanCommit
     ️1:事务询问
     协调者向所有的参与者发送一个包含事务内容的canCommit请求,询问是否可以执行事务操作,并开始等待各参与者的响应
    
    2.各参与者向协调者反馈事务询问的响应
    参与在接收在接收来自协调者的包含了事务内容的canCommit请求后。
    正常情况下,如果自身认为可以顺利执行事务,则反馈Yes响应,并进入预备状态,否则反馈NO响应
    
    阶段二:PreCommit
    协调者得到所有事务参与者的响应结果,根据结果的,协调者会出现两种执行情况。执行事务预提交。
    或者中间事务,如果接受到都是Yes就执行事务。
    	1.执行事务预提交分为3个步骤
    		1.1发送预提交请求:
    		协调者向所有参与者节点发出preCommit请求,并进入prepared阶段
    		
    		1.2事务预提交
    		参与者接受到preCommit请求后,会执行事务操作,并将Undo和Redo信息记录到事务日志中
    		
    		1.3各参与者向协调者反馈事务执行的结果:
    		若参与者成功执行了事务操作,那么反馈Ack
    	
    若任一参与者反馈了No相应,或者在等待超时后,协调者还无法接收所有参与者反馈,则中断事务
    	2.中断事务也分为两个步骤
    		2.1发送中断请求
    		协调者向所有参与者发出abort请求
    		2.2中断事务
    		无论是收到来自协调的abort请求或者等待协调者请求过程中超时,参与者都会中断事务
    		```
    ```java
    阶段三:do commit
    ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210326162136165.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0ZEWENHX1g=,size_16,color_FFFFFF,t_70)
    
    该阶段做真正的事务提交或者完成事务回滚,所以就会出现两种情况
    	1.执行事务提交
    		1.1发送提交请求
    		如果协调者处于正常工作状态,并且它接收到了来自所有参与者的Ack响应,那么他将从预提交状态转换为提交状态,并向所有的参与者发送doCommit请求
    		1.2事务提交
    		参与者接收到doCommit请求后,会正式执行事务提交操作,并在完成提交之后释放整个事务执行过程中占用的事务资源
    		1.3反馈事务提交结果
    		参与者在完成事务提交后,向协调者发送Ack响应
    	
    		1.4完成事务:协调者接收到所有参与者反馈的Ack消息后,完成事务
    	
    	2.中断事务
    		2.1发送中断请求:协调者向所有的参与者节点发送abort请求
    		2.2事务回滚:参与者收到abort请求后,会根据记录的Undo信息来执行事务回滚。并在完成回滚之后释放整
    个事务执行期间占用的资源
    		2.3 反馈事务回滚结果:参与者在完成事务回滚后,向协调者发送Ack消息
    		2.4中断事务:协调者接收到所有参与者反馈的Ack消息后,中断事务
    		
    注意:一旦进入阶段三,可能会出现 2 种故障
    	1. 协调者出现问题
    	2. 协调者和参与者之间的网络故障
    如果出现了任一一种情况,最终都会导致参与者无法收到 doCommit 请求或者 abort 请求,针对这种情况,参与
    者都会在等待超时之后,继续进行事务提交
    

    3PC的优点和缺点:

    优点:
    1.首先对于协调者和参与者都设置了超时机制(在2PC中,只有协调者拥有超时机制,即如果在一定时间内没有收
    到参与者的消息则默认失败),主要是避免了参与者在长时间无法与协调者节点通讯(协调者挂掉了)的情况下,无
    法释放资源的问题,因为参与者自身拥有超时机制会在超时后,自动进行本地commit从而进行释放资源。而这种
    机制也侧面降低了整个事务的阻塞时间和范围。
    
    2.通过CanCommit、PreCommit、DoCommit三个阶段的设计,
    相较于2PC而言,多设置了一个缓冲阶段保证了在最后提交阶段之前各参与节点的状态是一致的
    
    3.PreCommit是一个缓冲,保证了在最后提交阶段之前各参与节点的状态是一致的
    

    在这里插入图片描述

    展开全文
  • 由于分布式应用中的网络的不确定性,P必须满足,而CAP一般只能满足其中的个,故一般分布式应用只能满足AP或CP,无法同时实现CAP。 zookeeper作为一个分布式的协调服务,实现的是CP,实现了最终一致性。

    我们知道,一般在分布式应用都有三个准则:

    1. C Consistency, 一致性,分布式中的各个节点数据保持一致
    2. A availability 可用性,任何时刻,分布式集群总是能够提供服务
    3. P partition tolerance,分区容错性,由于网络分区的存在,当某个网络分区出现问题的时候,依然能够运行

    CAP理论:CAP无法同时满足,只能同时满足其中的两个,我们假设有三个节点a,b,c,这时候因为网络故障分为两组【a,b】,【c】,为了支持分区容错P,处理请求时:

    1. 保证一致性 C 的时候,这时候需要保证所有节点的数据一致,需要将数据复制到所有节点,但是这时候网络故障,无法复制到c节点,只能返回失败给客户端,这时候系统处于不可用状态,无法满足A
    2. 如果要保证可用性A,这时候数据只需要复制到a,b节点,c节点不需要复制,这时候可能c也可能处理请求,c的数据也无会复制到a,b节点,这时候【a,b】,【c】数据就不一致了

    BASE理论:即使无法做到强一致性,也需要采取适当的措施来达到最终一致性,其有三项指标:

    • Basically Available 基本可用,指的是分布式系统出现不可预知的故障的时候,允许损失部分可用性,但是整个系统对外仍然可用
    • Soft state 弱状态,允许系统中的数据存在中间状态,并且该中间状态不会影响系统整体的可用性,即允许系统在不同节点之间进行数据同步存在延时
    • Eventually consistent 最终一致性,系统中所有节点在经过一段时间的同步后最终能够达到一个一致的状态。因此最终一致性的本质是需要系统保证最终数据能够达到一致,而不需要实时保证数据的强一致性。

    分布式系统的架构设计过程中,往往需要对一致性和可用性 CA之间进行反复的权衡,基于此产生一系列的一致性协议和算法,其中比较有名的就是两阶段提交协议、三阶段提交协议、Paxos算法

    两阶段提交 2PC

    2pc 即 Two-Phase Commit,为了使分布式系统架构下的所有节点进行实务的过程中能够保持原子性和一致性的而设计的一种算法。

    2PC 阶段一 ,提交实务请求
    • 协调者向所有的参与者发送实务内容,询问是否可以实务操作,并等待参与者的响应
    • 各参与者节点执行实务,注意这时候只是执行了事务,事务并未提交,然后参与者向协调者反馈,如果执行成功返回OK,否则NO
    2PC 阶段二 ,执行事务提交
    • 如果所有参与者的反馈都是OK,那么执行事务提交,协调者向所有参与者发出commit请求
    • 参与者收到commit请求之后执行实务提交,执行完之后向协调者反馈ACK信息
    • 协调者收到所有参与者的ACK反馈之后,完成事务

    如果在第一阶段中有参与者的反馈是NO的话,那么需要中断事务:

    • 协调者向所有参与者发送回滚请求rollback
    • 所有参与者收到回滚请求rollback之后回滚事务,并在回滚之后反馈结果
    • 协调者收到所有参与者的反馈,事务中断完成

    二阶段提交存在的问题:

    • 同步阻塞,执行过程中,所有参与节点都是事务阻塞型的,各个参与者在等待其他参与者的响应过程中无法进行其他操作
    • 单点问题,协调者的角色很重要,一旦协调者出现问题,整个过程都无法继续,如果在阶段二中出现问题,参与者将一直处于锁定事务资源的状态中,无法继续完成事务操作
    • 数据不一致 ,在阶段二的时候,当协调者向所有参与者发送Commit请求之后,发生了网络异常,导致只有部分参与者收到了Commit请求,于是,这部分收到了Commit请求的参与者提交了事务,而其他没有收到Commit请求的参与者则无法提交事务,导致整个系统出现了数据不一致现象

    三阶段提交 ,3PC

    三阶段提交是两阶段提交的改进型,进行了如下改进:

    • 引入了超时机制
    • 在2PC的第一阶段和第二阶段中插入了一个准备阶段,将提交事务请求一分为二

    3PC由:CanCommit,PreCommit,doCommit三个阶段组成

    阶段一 , CanCommit
    • 协调者向所有参与者发送CanCommit请求,询问是否可以执行事务提交操作,并等待参与者的反馈
    • 各个参与者收到协调者的CanCommit请求之后,查看自身状态是否可以顺利执行事务,正常返回OK,否则NO
    阶段二 , PreCommit

    如果阶段一各参与者反馈OK,开始阶段二

    • 协调者向所有参与者发送PreCommit请求,进入Prepared阶段
    • 参与者收到preCommit请求后,执行事务操作,将Undo和Redo记录到事务日志,这时候并未commit事务,然后向协调者反馈,返回OK or NO

    如果任意一个参与者返回了NO,或者协调者等待参与者反馈超时,这时候中断事务:

    • 协调者向所有参与者发送abort请求
    • 参与者收到abort请求,或者等待协调者请求出现超时都会abort事务
    阶段三 , DoCommit

    这时候开始进行真正的事务提交,如果阶段二中所有参与返回OK,则:

    • 协调者从Prepared状态进入Commit状态,向所有参与者发送DoCommit请求
    • 参与者收到协调者的DoCommit请求时候,进行事务提交,并返回信息给协调者
    • 协调者收到所有参与者的反馈后,完成事务

    如果这个阶段协调者正常工作,并且任意一个参与者返回了NO,或者协调者等待参与者反馈超时,这时候中断事务:

    • 协调者向所有参与者发送abort请求
    • 参与者收到abort请求后,会根据在阶段二中记录的undo信息执行事务回滚,之后反馈协调者
    • 协调者收到所有参与者反馈,中断事务

    需要注意的是,一旦进入阶段三,可能存在下面两种故障:

    • 协调者出现问题
    • 协调者和参与者之间出现网络故障

    无论出现上述哪种情况,都会导致参与者无法及时接收到协调者的doCommit或者abort请求,这时候参与者会在等待超时之后进行事务的提交
    三阶段的优点:相比两阶段,降低了参与者的阻塞范围,并且能够在出现单点故障后继续达到一致
    三阶段的缺点:参与者接收到preCommit请求之后,如果出现网络分区问题,此时协调者和参与者无法通信,这时候该参与者依然会进行事务的提交,会导致数据不一致。

    Paxos算法

    Paxos算法的核心是一个一致性算法,是一个经典、完备的分布式一致性算法,其目标是在不同的节点之前,对同一个key的取值达成共识。
    在Paxos算法中,有如下三种角色:

    • Proposer 提案者,对key值提出自己的值
    • Acceptor 决议者,对提案者的提议进行投票,选定一个提案形成最终的决策
    • Leaner 学习者,学习决议者达成共识的决策

    Paxos算法分为两个阶段:

    阶段一 准备阶段,Prepare请求

    Porposer选择一个新的提案编号N,然后向Acceptor过半集合成员发送Prepare请求,要求该集合中的Acceptor做出如下回应:

    • 向Proposer承诺,保证不再批准任何编号小于N的提案
    • 如果Acceptor已经批准过任何提案,那么就应该向Proposer反馈当前Acceptor已经批准的编号小于N的最大的提案的值
    阶段二 批准提案,Accept请求

    1)如果Proposer收到了来自半数以上的Acceptor的响应结果,那么就可以产生编号为N,Value值为Vn的天,这里的Vn死所有相中编号最大的提案的值,如果半数以上的Acceptor都没有批准过任何提案,这时候Vn的值由Proposer任意选择
    2) 如果Acceptor收到了[N ,Vn]的提案的Accept请求,只要改Acceptor尚未对编号大于N的天的Prepare请求做出响应,它就可以通过这个提案

    Paxos协议中一个提案被通过后,这个提案的值被选中,后面按照协议继续交互下去,这个值一直保持不变,也就是一直一致,其目标是保证最终有一个提案会被通过,提案通过后,其他Acceptor最终也只能获取通过的提案,一言蔽之,将所有节点都写入同一个值,且被写入后不再更改

    Paxos 活锁:两个Proposer交替请求,编号不断变大,但是无法通过任何一个提案

    Raft算法

    Raft算法是Paxos算法的一种简化实现,有三种角色

    • Leader 领导者,所有写入都是通过leader写入,同时leader会强制所有follower来同步数据
    • Candidate 选举的状态,会向其他所有节点拉选票,如果得到大部分选票,则会变成Leader
    • Follower 所有节点开始都是Follower状态,如果没有收到Leader的消息则会变成Candidate
      其他概念:
      Term: 连续单调递增的编号,每个term代表一轮选举
      随机心跳时间:Follower节点每次收到Leader节点心跳请求之后都会设置一个区间在[150ms ,300ms]的超时时间,如果超过这个时间还没收到心跳,则认为Leader故障

    Raft算法是通过选出一个Leader来简化副本之间的数据同步,将一致性分为了两个重要个子逻辑:

    • Leader选举
    • 日志复制
    Leader选举过程
    1. 当Follower节点在超时时间内没有收到Leader心跳,则认为当前Leader故障,将从Follower状态变为Candidate,更新本地的Current Term值,同时批量向其他节点发送拉票请求
    2. 其他Follower节点收到请求后,判断请求的Term大于自己的Current Term,且请求的log序号不低于本地,则认为这张票是合法的(每轮term只能投一次票)
      3)如果Candidate收到了超过半数的投票,则晋升为Leader,同时立刻给所有节点发送消息,避免其余节点触发新的选举,然后开始日志同步、处理客户端请求
    日志复制
    1. Leader收到客户端的数据请求后,先把数据写到本地日志(这时候尚未提交),同时向Follower节点发送AppendEntries,将请求的数据发送给Follower
    2. Follower收到请求数据判断如果没有冲突的话,则将数据写入日志(未提交),然后反馈给Leader
    3. Leader收到Follower的反馈如果超过半数成功,则提交本地未提交的事务,(这时候就可以给客户端响应了),然后向Follower再次发送AppendEntries请求
    4. Follower收到请求后提交本地事务

    zookeeper作为一个分布式的协调服务,实现的是CP,实现了最终一致性。
    在zookeeper中有如下几个角色:

    1. Leader节点,领导者,集群所有的写入都通过leader写入,维护与Follower和Observer之间的心跳,同时会将写入的数据广播给其他节点,zookeeper集群同一时间只会有一个工作的Leader
    2. Follower节点,与Leader保持心跳,Follower节点可以直接处理客户端的读请求,可以接受写请求,但是无法处理读请求,会将读请求转发给Leader进行处理。另外还会参与投票,包含Leader写请求的投票以及选主的投票
    3. Observer节点,与Follower基本一样,但是没有投票权

    zookeeper的核心是原子广播(zookeeper automic broadcast,ZAB原子消息广播协议)。讲解这部分之前,我们先了解zk中几个属性:

    1. myid,每个zk节点在集群中的唯一id,我们在安装配置zk的时候都会写入一个唯一id到myid的配置文件汇总
    2. zxid,zk的事务id,用来表示每次更新操作的提案(Proposal ID),该ID单调递增,保证了顺序一致性,zk使用64位来表示zxid,其中高32位表示Leader的epoch,从1开始,每次Leader变化选出新的Leader之后,epoch 加一,低32位表示的是该epoch内的事务id,单调递增,每次epoch发生改变,低32位重置。

    zk为了保证提案的按zxid顺序生效,使用了一个ConcurrentHashMap,记录所有未提交的提案,key为zxid,value为提案的信息。当有事务发生的时候,Leader节点会把数据通过proposal请求发送到所有的节点上面,所有收到proposal的节点接收到数据以后会把数据写入到本地磁盘,然后发送ACK给leader,leader节点判断过半节点都返回了ACK,会发送commit消息给各个节点,各节点就会把消息放入内存中。

    可以看到,zk的写操作,类似两阶段提交。

    zk在选主的时候,会向集群中其他节点发送下面的信息:

    • logicClock,灭节点维护的自增整数,表示的是当前节点发起的第多少轮投票,zk的每次选举必须在同一轮中,就是logicClock必须一致
    • state 当前节点的状态
    • self_id 当前节点的myid
    • self_zxid 当前节点上保存数据的最大的zxid
    • vote_id 被推举的节点的myid
    • zote_zxid 被推举的节点保存的数据的最大zxid

    选主大致流程:

    1. 每个节点在开始投票的时候会将自己的logicClock自增加一
    2. 清空节点自身的投票箱
    3. 发送投票给自己,初始的时候,每个节点都是投给自己
    4. 接收外部投票
    5. 收到外部投票后,判断选举轮次logicClock,
    • 如果当前节点的logicClock比收到logicClock小,更新当前节点logicClock到收到的logicClock,然后在比较之前的投票与当前投票确定是否需要变更自己的投票,然后将自己的投票广播出去
    • 外部投票的logicClock小于当前节点的logicClock,直接忽略该投票
    • 外部投票的logicClock与当前节点的logicClock相等,进行选票处理
    1. 选票处理。选票的处理是基于(self_id,self_zxid)与(vote_id,vote_zxid),首先比较外部选票与自己的选票的self_zxid,如果外部选票的vote_zxid比自己的选票的vote_zxid大,则将自己手中的选票的vote_id,vote_zxid都更新为收到的外部选票;如果这两张选票的vote_zxid一致,则比较二者的vote_myid,哪个大,将vote_id,vote_zxid更新为vote_zxid大的那个信息,然后将自己的选票广播出去
    2. 统计选票,若有过半的服务器认可了自己的投票,则终止投票
    3. 更新节点状态

    zk集群节点存在四种状态:

    • LOOKING,一般这时候没有确定Leader,会发起选主
    • LEADING,领导者状态,当前节点是Leader节点
    • FOLLOWING,跟随者状态,当前节点是Follower节点
    • OBSERVERING,观察者状态,当前节点是Observer节点

    综上来看,对比Raft算法和ZAB算法:

    1. 在客户端请求写入数据时,zab算法是采用类似两阶段提交,只有过半的节点都写入了事务,才返回响应给客户端,但是Raft算法,在Prepare阶段收到过半节点的请求后自身提交事务就返回给客户端了,这时候过半节点的事务并未提交,而是后面Leader在给Follower提交请求,如果Leader返回客户端之后立马挂了,这时候节点间数据就不一致了
    2. Raft在日志的复制过程中,心跳是从Leader到Follower,ZAB则相反
    展开全文
  • 目录 一、2PC 1.1、提交事务请求(投票阶段) 1.2、执行事务提交 ...1.1、提交事务请求(投票阶段) ...事务询问:由协调者向所有参与者发送事务内容,询问是否可以执行事务提交操作 ...该阶段拥有种操作:.

    目录

     

    一、2PC

    1.1、提交事务请求(投票阶段)

    1.2、执行事务提交


    一、2PC

    1.1、提交事务请求(投票阶段)

    事务询问->执行事务->反馈事务询问

    事务询问:由协调者向所有参与者发送事务内容,询问是否可以执行事务提交操作

    执行事务:由参与者执行事务操作,并将Undo(拒绝)和Redo(准备好,将要操作)信息记入事务日志中

    反馈事务询问:由参与者给协调者反馈给协调者事务的执行结果,如果执行成功反馈YES,否者反馈NO

    1.2、执行事务提交

    该阶段拥有两种操作:执行事务提交、中断事务

    执行事务提交:发送提交请求->事务提交->反馈事务提交结果->完成事务

    中断事务:发送回滚请求->事务回滚->反馈事务回滚结果->中断事务

    总结:二阶段提交将事务处理过程分成两个过程,分别是投票和执行两个阶段,其核心是对每个事务先尝试后提交的处理方式,该提交方式是一个强一致性的算法。

    优点:原理简单、实现方便

    缺点:同步阻塞、单点问题、脑裂、太过保守

    二、3PC

    21、CanCommit(提交询问)

    事务询问:协调者向参与者发送一个包含事务内容的CanCommit请求,询问是否可执行事务提交操作

    反馈事务询问结果:参与者返回CanCommit询问结果

    2.2、PreCommit

    包含两种操作:执行事务预提交和中断事务

    执行事务预提交:发送预提交请求->参与者事务预提交->参与者反馈预提交结果

    中断事务:发送中断请求->中断事务

    2.3、DoCommit(事务提交)

    包含两种操作:执行提交、中断事务

    执行提交:发送提交请求->事务提交->反馈事务提交结果->完成事务

    中断事务:发送中断请求->事务回滚->反馈事务回滚结果->中断事务

     

    总结:相比于两阶段提交,增加一个询问阶段,该阶段的好处是可以让协调者尽早的发现发问题。同时在询问阶段不会锁定资源,降低资源的锁定范围。

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

    展开全文
  • 两阶段提交(two-phase commit, 2PC)是最基础的分布式一致性协议,应用广泛。本文来介绍它的相关细节以及它在Flink中的典型应用场景。2PC简介先介绍个前置概念。在分布式系统中,为了让每个节点都能够感知到其他...

    前言

    简书快正式从小黑屋里出来了,所以是时候重启更新了。这段时间积攒了不少要写的东西,逐个击破吧。

    两阶段提交(two-phase commit, 2PC)是最基础的分布式一致性协议,应用广泛。本文来介绍它的相关细节以及它在Flink中的典型应用场景。

    2PC简介

    先介绍两个前置概念。在分布式系统中,为了让每个节点都能够感知到其他节点的事务执行状况,需要引入一个中心节点来统一处理所有节点的执行逻辑,这个中心节点叫做协调者(coordinator),被中心节点调度的其他业务节点叫做参与者(participant)。

    接下来正式介绍2PC。顾名思义,2PC将分布式事务分成了两个阶段,两个阶段分别为提交请求(投票)和提交(执行)。协调者根据参与者的响应来决定是否需要真正地执行事务,具体流程如下。

    提交请求(投票)阶段

    协调者向所有参与者发送prepare请求与事务内容,询问是否可以准备事务提交,并等待参与者的响应。

    参与者执行事务中包含的操作,并记录undo日志(用于回滚)和redo日志(用于重放),但不真正提交。

    参与者向协调者返回事务操作的执行结果,执行成功返回yes,否则返回no。

    提交(执行)阶段

    分为成功与失败两种情况。

    若所有参与者都返回yes,说明事务可以提交:

    协调者向所有参与者发送commit请求。

    参与者收到commit请求后,将事务真正地提交上去,并释放占用的事务资源,并向协调者返回ack。

    协调者收到所有参与者的ack消息,事务成功完成。

    若有参与者返回no或者超时未返回,说明事务中断,需要回滚:

    协调者向所有参与者发送rollback请求。

    参与者收到rollback请求后,根据undo日志回滚到事务执行前的状态,释放占用的事务资源,并向协调者返回ack。

    协调者收到所有参与者的ack消息,事务回滚完成。

    下图分别示出这两种情况。

    02d6d1103746

    提交成功

    02d6d1103746

    提交失败

    2PC的优缺点

    2PC的优点在于原理非常简单,容易理解及实现。缺点主要有3个,列举如下:

    协调者存在单点问题。如果协调者挂了,整个2PC逻辑就彻底不能运行。

    执行过程是完全同步的。各参与者在等待其他参与者响应的过程中都处于阻塞状态,大并发下有性能问题。

    仍然存在不一致风险。如果由于网络异常等意外导致只有部分参与者收到了commit请求,就会造成部分参与者提交了事务而其他参与者未提交的情况。

    不过,现在人们在分布式一致性领域做了很多工作,以ZooKeeper为代表的分布式协调框架也数不胜数,2PC有了这些的加持,可靠性大大提升了,也就能够真正用在要求高的生产环境中了。下面看看2PC与Flink是怎么扯上关系的。

    Flink基于2PC的事务性写入

    2PC的最常见应用场景其实是关系型数据库,比如MySQL InnoDB存储引擎的XA事务系统。但我对MySQL研究不是很深入,也就不多废话了,详情参见MySQL官方文档。

    Flink作为流式处理引擎,自然也提供了对exactly once语义的保证。在之前关于Spark Streaming exactly once的文章中曾经提到过,端到端的exactly once语义,是输入、处理逻辑、输出三部分协同作用的结果。Flink内部依托检查点机制和轻量级分布式快照算法ABS保证exactly once。而要实现精确一次的输出逻辑,则需要施加以下两种限制之一:幂等性写入(idempotent write)、事务性写入(transactional write)。

    在Spark Streaming中,要实现事务性写入完全靠用户自己,框架本身并没有提供任何实现。但是在Flink中提供了基于2PC的SinkFunction,名为TwoPhaseCommitSinkFunction,帮助我们做了一些基础的工作。

    02d6d1103746

    TwoPhaseCommitSinkFunction的继承关系

    Flink官方推荐所有需要保证exactly once的Sink逻辑都继承该抽象类。它定义了如下4个抽象方法,需要子类实现。

    protected abstract TXN beginTransaction() throws Exception;

    protected abstract void preCommit(TXN transaction) throws Exception;

    protected abstract void commit(TXN transaction);

    protected abstract void abort(TXN transaction);

    beginTransaction():开始一个事务,返回事务信息的句柄。

    preCommit():预提交(即提交请求)阶段的逻辑。

    commit():正式提交阶段的逻辑。

    abort():取消事务。

    下面以Flink与Kafka的集成来说明2PC的具体流程。注意这里的Kafka版本必须是0.11及以上,因为只有0.11+的版本才支持幂等producer以及事务性,从而2PC才有存在的意义。Kafka内部事务性的机制如下框图所示。这就是另外一个大的话题了,本文不表。

    02d6d1103746

    开始事务

    粗略看看FlinkKafkaProducer011类实现的beginTransaction()方法。

    @Override

    protected KafkaTransactionState beginTransaction() throws FlinkKafka011Exception {

    switch (semantic) {

    case EXACTLY_ONCE:

    FlinkKafkaProducer producer = createTransactionalProducer();

    producer.beginTransaction();

    return new KafkaTransactionState(producer.getTransactionalId(), producer);

    case AT_LEAST_ONCE:

    case NONE:

    // Do not create new producer on each beginTransaction() if it is not necessary

    final KafkaTransactionState currentTransaction = currentTransaction();

    if (currentTransaction != null && currentTransaction.producer != null) {

    return new KafkaTransactionState(currentTransaction.producer);

    }

    return new KafkaTransactionState(initNonTransactionalProducer(true));

    default:

    throw new UnsupportedOperationException("Not implemented semantic");

    }

    }

    可见,当要求exactly once语义时,会调用createTransactionalProducer()生成包含事务ID的producer。

    预提交阶段

    FlinkKafkaProducer011.preCommit()方法的实现很简单。其中的flush()方法实际上是代理了KafkaProducer.flush()方法。

    @Override

    protected void preCommit(KafkaTransactionState transaction) throws FlinkKafka011Exception {

    switch (semantic) {

    case EXACTLY_ONCE:

    case AT_LEAST_ONCE:

    flush(transaction);

    break;

    case NONE:

    break;

    default:

    throw new UnsupportedOperationException("Not implemented semantic");

    }

    checkErroneous();

    }

    那么preCommit()方法是在哪里使用的呢?答案是TwoPhaseCommitSinkFunction.snapshotState()方法。从前面的类图可以得知,TwoPhaseCommitSinkFunction也继承了CheckpointedFunction接口,所以2PC是与检查点机制一同发挥作用的。

    @Override

    public void snapshotState(FunctionSnapshotContext context) throws Exception {

    // this is like the pre-commit of a 2-phase-commit transaction

    // we are ready to commit and remember the transaction

    checkState(currentTransactionHolder != null, "bug: no transaction object when performing state snapshot");

    long checkpointId = context.getCheckpointId();

    LOG.debug("{} - checkpoint {} triggered, flushing transaction '{}'", name(), context.getCheckpointId(), currentTransactionHolder);

    preCommit(currentTransactionHolder.handle);

    pendingCommitTransactions.put(checkpointId, currentTransactionHolder);

    LOG.debug("{} - stored pending transactions {}", name(), pendingCommitTransactions);

    currentTransactionHolder = beginTransactionInternal();

    LOG.debug("{} - started new transaction '{}'", name(), currentTransactionHolder);

    state.clear();

    state.add(new State<>(

    this.currentTransactionHolder,

    new ArrayList<>(pendingCommitTransactions.values()),

    userContext));

    }

    结合Flink检查点的原理,可以用下图来形象地表示预提交阶段的流程。

    02d6d1103746

    图来自Flink官方博客:https://flink.apache.org/features/2018/03/01/end-to-end-exactly-once-apache-flink.html

    一旦开启了checkpoint功能,JobManager就在数据流中源源不断地打入屏障(barrier),作为检查点的界限。屏障随着算子链向下游传递,每到达一个算子都会触发将状态快照写入状态后端的动作。当屏障到达Kafka sink后,通过KafkaProducer.flush()方法刷写消息数据,但还未真正提交。接下来还是需要通过检查点来触发提交阶段。

    提交阶段

    FlinkKafkaProducer011.commit()方法实际上是代理了KafkaProducer.commitTransaction()方法,正式向Kafka提交事务。

    @Override

    protected void commit(KafkaTransactionState transaction) {

    if (transaction.isTransactional()) {

    try {

    transaction.producer.commitTransaction();

    } finally {

    recycleTransactionalProducer(transaction.producer);

    }

    }

    }

    该方法的调用点位于TwoPhaseCommitSinkFunction.notifyCheckpointComplete()方法中。顾名思义,当所有检查点都成功完成之后,会回调这个方法。

    @Override

    public final void notifyCheckpointComplete(long checkpointId) throws Exception {

    Iterator>> pendingTransactionIterator = pendingCommitTransactions.entrySet().iterator();

    checkState(pendingTransactionIterator.hasNext(), "checkpoint completed, but no transaction pending");

    Throwable firstError = null;

    while (pendingTransactionIterator.hasNext()) {

    Map.Entry> entry = pendingTransactionIterator.next();

    Long pendingTransactionCheckpointId = entry.getKey();

    TransactionHolder pendingTransaction = entry.getValue();

    if (pendingTransactionCheckpointId > checkpointId) {

    continue;

    }

    LOG.info("{} - checkpoint {} complete, committing transaction {} from checkpoint {}",

    name(), checkpointId, pendingTransaction, pendingTransactionCheckpointId);

    logWarningIfTimeoutAlmostReached(pendingTransaction);

    try {

    commit(pendingTransaction.handle);

    } catch (Throwable t) {

    if (firstError == null) {

    firstError = t;

    }

    }

    LOG.debug("{} - committed checkpoint transaction {}", name(), pendingTransaction);

    pendingTransactionIterator.remove();

    }

    if (firstError != null) {

    throw new FlinkRuntimeException("Committing one of transactions failed, logging first encountered failure",

    firstError);

    }

    }

    该方法每次从正在等待提交的事务句柄中取出一个,校验它的检查点ID,并调用commit()方法提交之。这阶段的流程可以用下图来表示。

    02d6d1103746

    可见,只有在所有检查点都成功完成这个前提下,写入才会成功。这符合前文所述2PC的流程,其中JobManager为协调者,各个算子为参与者(不过只有sink一个参与者会执行提交)。一旦有检查点失败,notifyCheckpointComplete()方法就不会执行。如果重试也不成功的话,最终会调用abort()方法回滚事务。

    @Override

    protected void abort(KafkaTransactionState transaction) {

    if (transaction.isTransactional()) {

    transaction.producer.abortTransaction();

    recycleTransactionalProducer(transaction.producer);

    }

    }

    The End

    晚安(好像有点早啊喂

    展开全文
  • 2PC(two phase commit protocol,2PC)即两阶段提交协议,是将整个事务流程分为个阶段,准备阶段(Prepare phase)、提交阶段(Commit phase),2指个阶段,P指准备阶段,C指提交阶段。整个事务过程由事务管理...
  • TCC协议(Try, Confirm, Cancel) TCC编程: 将业务逻辑拆解为Try、Confirm和Cancel三个阶段,实际上两阶段提交的变种。业务场景如:锁单、下单和取消。 TCC编程需要保证幂等性,即:可以被不断调用接口(直至符合预期...
  • 阶段提交协议 三阶段提交主要解决了二阶段提交的缺点。... 优点:三阶段提交协议可以有效避免阻塞情况的出现,因为不管是协调者也好,还是参与者也好,都增加了超时机制。 缺点:相对于2PC,3P...
  • flink的二阶段提交

    2021-01-20 00:30:02
    场景描述:两阶段提交(two-phase commit, 2PC)是最基础的分布式一致性协议,应用广泛。本文来介绍它的相关细节以及它在Flink中的典型应用场景。。简介:2PC 在分布式系统中,为了让每个节点能够感知其他所有节点的...
  • 2PC即两阶段提交协议,是将整个事务流程分为个阶段,准备阶段(Prepare phase)、提交阶段(commit phase),2是指个阶段,P是指准备阶段,C是指提交阶段。 举例:张三和李四好久不见,老友约起聚餐,饭店老板...
  • 段锁协议

    2021-02-26 17:15:42
    两阶段协议   在数据库系统领域,并发控制机制主要有种,即锁和多版本机制。   1.事务在加锁时有多种方式:   一次性锁协议,事务开始时,即一次性申请所有的锁...
  • 阶段提交协议两阶段提交协议的改进版本,它通过超时机制解决了阻塞的问题,并且把个阶段增加为三个阶段。 流程 询问阶段 协调者询问参与者是否可以完成指令,协调者只需要回答是或不是,而不需要做真正的操作...
  • 文章目录分布式事务专题-2PC阶段提交(3)1. 什么是2PC2. 解决方案2.1 XA方案2.2 Seata方案3. seata实现的2PC事务4....2PC即两阶段提交协议,是将整个事务流程分为个阶段,准备阶段(Prepare phase)、提交阶
  • 五、分布式事务 - 2PC 2PC(two-phase commit protocol,两阶段提交协议),2PC 是一个非常经典的强一致、中心化的原子提交协议。 这里所说的中心化是指协议中有类节点:一个是中心化协调者节点(coordinator)和...
  • MySQL 与 MVCC MySQL 中实现的多版本两阶段协议(Multiversion 2PL)将 MVCC 和 2PL 的优点结合了起来,每一个版本的数据行都具有一个唯一的时间戳,当有读事务请求时,数据库程序会直接从多个版本的数据项中具有...
  • 2PC(Two-Phase Commit 二阶段提交)二阶段提交,是指将事务提交分成个部分:准备阶段和提交阶段。事务的发起者称之为协调者,事务的执行者称为参与者。阶段一:准备阶段由协调者发起并传递带有事务信息的...
  • JAVA中的分布式事务03-3PC(三阶段提交)方案简介​ 三阶段提交协议,是二阶段提交协议的改进版本,与二阶段提交不同的是,引入超时机制。同时在协调者和参与者中都引入超时机制。​ 三阶段提交将二阶段的准备阶段拆分...
  • [二段式提交协议]是将事务的提交过程分成了阶段来进行处理,其执行过程如下: 阶段一:提交事务请求: 1、事务询问。协调者向所有参者发送事务内容,询问是否可以进行事务提交操作,然后就开始等待参与者的...
  • 一致性协议

    2020-12-25 17:05:10
    顾名思义,二阶段提交协议是将事务的提交过程分成了个阶段来进行处理,其执行流 程如下。 阶段一:提交事务请求 1.事务询问。 协调者向所有的参与者发送事务内容,询问是否可以执行事务提交操作,并开始等待各参与...
  • 一、 一致性协议概述 前面已经讨论过,在分布式环境下,有很多不确定性因素,故障随时都回发生,也讲了CAP理论,BASE理论 我们希望达到,在分布式环境下能搭建一个高可用的,且数据高一致性的服务,目标是这样,但...
  • 网络协议面试题

    2021-09-23 21:46:29
    1. 什么是TCP/IP和UDP协议 TCP/IP协议即传输控制/网络协议,是面向连接的协议,发送数据之前需要建立连接,TCP提供可靠传输服务。 UCP协议即用户数据报协议,属于TCP/IP协议族中的一种。是无连接的协议,发送数据...
  • 点击上方“朱小厮的博客”,选择“设为星标”后台回复"书",获取后台回复“k8s”,可领取k8s资料协议安全和加密越来越引起人们的重视和关注,今天就跟大家分享一点协议加密方面...
  • 具体实现协议3.1 分布式一致性协议3.1.1 多主协议3.1.2 单主协议:3.2 分布式事务4. 碎碎念5. 参考资料 1. 写在最前面 从隔壁大佬那边借来了 zookeeper 的协议 zap 的论文介绍,原本想对比一下 zap 和 raft 协议的...
  • 接口和协议 1.软件开发的种结构 CS:客户端----服务器结构 C/S结构在技术上很成熟,它的主要特点是交互性强、具有安全的存取模式、网络通信量低、响应速度快、利于处理大量数据。 优缺点: 能充分发挥客户端PC的...
  • Casper FFG ETH准备采用的PoS协议为Casper the Friendly Finality Gadgest(Casper FFG),在过渡阶段也需要混合 PoW共同使用,为工作量证明提供Finality。单纯的基于PoW的交易会被回滚取消,而Finality是一种最终...
  • 引言现目前处理分布式事务的方案有很多,比如基于 XA 协议的方案基于 TCC 协议方案基于 SAGA 协议的方案而实现了对应的协议的有在 Java 中基于 XA 协议实现的框架有 Atomikos,JOTM 等框架TCC 协议的框架有 ...
  • 系列文章目录 一:http协议和https协议的区别 ...3、http的连接很简单,是无状态的,https协议是由ssl+http协议构建的可进行加密串苏,身份验证的网络协议,比http协议安全。 4、http用的端口是80,https用的端口是4
  •   2PC,是Two-Phase Commit的缩写,即二阶段提交。目前,绝大部分的关系型数据库都采用2PC协议来完成分布式事务处理的,利用该协议能够非常方便地完成所有分布式事务,统一决定事务的提交或回滚,从而能够有效地...
  • 交易所数据协议

    2021-04-05 09:41:47
    STEP/FIX/FAST协议 :沪深交易所 期货交易数据交换协议(FTD)协议: 上期所 TCP/UDP : 交易用TCP;行情先进的是UDP组播,原先也有用TCP的。
  •   3PC,是Three-Phase Commit的缩写,即三阶段提交,是2PC的改进版,将2PC协调的“提交事务请求”过程一分为二,形成了由CanCommit、PreCommit和do Commit三个阶段组成的事务处理协议。 1.3PC协议流程 (1)阶段一:...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 27,803
精华内容 11,121
关键字:

两阶段提交协议优点