分布式一致性问题:由X/Open DTP模型到2PC/3PC

版权声明:作者:TheLudlows 载请注明出处: https://blog.csdn.net/TheLudlows/article/details/81712973

分布式事务

举一个例子:A给B转100元。

然而A跟B很不幸的被分在了不同的数据库实例上。甚者这两个人可能是在不同机构开的户。这就是典型的分布式事务,从而也牵扯出分布式一致性问题。

前人在性能和数据一致性的反反复复权衡过程中总结了许多典型的协议和算法。其中比较著名的有二阶提交协议(Two Phase Commitment Protocol)、三阶提交协议(Two Phase Commitment Protocol)和Paxos算法。

分布式事务是指会涉及到操作多个数据库的事务。其实就是将对同一库事务的概念扩大到了对多个库的事务。目的是为了保证分布式系统中的数据一致性。分布式事务处理的关键是必须有一种方法可以知道事务在任何地方所做的所有动作,提交或回滚事务的决定必须产生统一的结果(全部提交或全部回滚)

在分布式系统中,各个节点之间在物理上相互独立,通过网络进行沟通和协调。由于存在事务机制,可以保证每个独立节点上的数据操作可以满足ACID。但是,相互独立的节点之间无法准确的知道其他节点中的事务执行情况。所以从理论上讲,两台机器理论上无法达到一致的状态。如果想让分布式部署的多台机器中的数据保持一致性,那么就要保证在所有节点的数据写操作,要不全部都执行,要么全部的都不执行。但是,一台机器在执行本地事务的时候无法知道其他机器中的本地事务的执行结果。所以他也就不知道本次事务到底应该commit还是 roolback。所以,常规的解决办法就是引入一个“协调者”的组件来统一调度所有分布式节点的执行。

XA规范

X/Open 组织定义了分布式事务处理模型。 X/Open DTP(X/Open Distributed Transaction Processing Reference Model) 模型包括应用程序( AP )、事务管理器( TM )、资源管理器( RM )。

  • AP: Application,应用程序。也就是业务层。哪些操作属于一个事务,就是AP定义的。
  • RM:Resource Manager,资源管理器。一般是数据库,也可以是其他的资源管理器,如消息队列(如JMS数据源),文件系统等
  • TM: Transaction Manager,事务管理器。接收AP的事务请求,对全局事务进行管理,管理事务分支状态,协调RM的处理,通知RM哪些操作属于哪些全局事务以及事务分支等等。这个也是整个事务调度模型的核心部分。

其中,AP 可以和TM 以及 RM 通信,TM 和 RM 互相之间可以通信,DTP模型里面定义了XA接口,TM 和 RM 通过XA接口进行双向通信,例如:TM通知RM提交事务或者回滚事务,RM把提交结果通知给TM。AP和RM之间则通过RM提供的Native API 进行资源控制,这个没有进行约API和规范,各个厂商自己实现自己的资源控制,比如Oracle自己的数据库驱动程序。

X/P

二阶提交协议和三阶提交协议就是根据这一思想衍生出来的。可以说二阶段提交其实就是实现XA分布式事务的关键(确切地说:两阶段提交主要保证了分布式事务的原子性:即所有结点要么全做要么全不做)

概念解释:

  • 事务:一个事务是一个完整的工作单元,由多个独立的计算任务组成,这多个任务在逻辑上是原子的。
  • 全局事务:对于一次性操作多个资源管理器的事务,就是全局事务
  • 分支事务:在全局事务中,某一个资源管理器有自己独立的任务,这些任务的集合作为这个资源管理器的分支任务
  • 控制线程:用来表示一个工作线程,主要是关联AP,TM,RM三者的一个线程,也就是事务上下文环境。简单的说,就是需要标识一个全局事务以及分支事务的关系。

2PC

二阶段提交的算法思路可以概括为:参与者将操作成败通知协调者,再由协调者根据所有参与者的反馈情报决定各参与者是否要提交操作还是中止操作。

所谓的两个阶段是指:

  • 第一阶段:准备阶段(投票阶段)
  • 第二阶段:提交阶段(执行阶段)

举个通俗的例子,西方教堂结婚的时候,都有这样的桥段:

  1. 牧师分别问新郎和新娘:你是否愿意……不管生老病死……(询问阶段)

  2. 当新郎和新娘都回答愿意后(锁定一生的资源),牧师就会说:我宣布你们……(事务提交)

1、准备阶段

事务协调者(事务管理器)给每个参与者(资源管理器)发送Prepare消息,每个参与者要么直接返回失败,要么在本地执行事务,写本地的redo和undo日志,但不提交,到达一种“万事俱备,只欠东风”的状态。如下图所示

2pc

  1. 协调者会问所有的参与者结点,是否可以执行提交操作。
  2. 各个参与者开始事务执行的准备工作:如:为资源上锁,预留资源,写undo/redo log……
  3. 参与者响应协调者,如果事务的准备工作成功,则回应“可以提交”,否则回应“拒绝提交”。
2、提交阶段

如果在准备阶段,所有的资源都返回ok,表示准备好了。那么事务管理器向每个资源发送提交(Commit)消息。

如果协调者收到了参与者的失败消息或者超时,直接给每个参与者发送回滚(Rollback)消息。

2pc

当协调者节点从所有参与者节点获得的相应消息都为”同意”时:

  1. 协调者节点向所有参与者节点发出”正式提交(commit)”的请求。
  2. 参与者节点正式完成操作,并释放在整个事务期间内占用的资源。
  3. 参与者节点向协调者节点发送”完成”消息。
  4. 协调者节点受到所有参与者节点反馈的”完成”消息后,完成事务。

如果任一参与者节点在第一阶段返回的响应消息为”中止”,或者 协调者节点在第一阶段的询问超时之前无法获取所有参与者节点的响应消息时:

  1. 协调者节点向所有参与者节点发出”回滚操作(rollback)”的请求。
  2. 参与者节点利用之前写入的Undo信息执行回滚,并释放在整个事务期间内占用的资源。
  3. 参与者节点向协调者节点发送”回滚完成”消息。
  4. 协调者节点受到所有参与者节点反馈的”回滚完成”消息后,取消事务。
2PC的思考

二阶段提交看起来确实能够提供原子性的操作,但是不幸的事,二阶段提交还是有几个缺点的:

  1. 如果第一阶段中,参与者没有收到询问请求,或是参与者的回应没有到达协调者。那么,需要协调者做超时处理,一旦超时,可以当作失败,也可以重试。
  2. 单点故障。由于协调者的重要性,一旦协调者发生故障。参与者会一直阻塞下去。尤其在第二阶段,协调者发生故障,那么所有的参与者还都处于锁定事务资源的状态中,而无法继续完成事务操作。(如果是协调者挂掉,可以重新选举一个协调者,但是无法解决因为协调者宕机导致的参与者处于阻塞状态的问题)
  3. 数据不一致。在二阶段提交的阶段二中,当协调者向参与者发送commit请求之后,发生了局部网络异常或者在发送commit请求过程中协调者发生了故障,这回导致只有一部分参与者接受到了commit请求。而在这部分参与者接到commit请求之后就会执行commit操作。但是其他部分未接到commit请求的机器则无法执行事务提交。于是整个分布式系统便出现了数据部一致性的现象。
  4. 二阶段无法解决的问题:协调者再发出commit消息之后宕机,而唯一接收到这条消息的参与者同时也宕机了。那么即使协调者通过选举协议产生了新的协调者,这条事务的状态也是不确定的,没人知道事务是否被已经提交。

最致命的就是如果第一阶段完成后,参与者在第二阶没有收到决策,那么数据结点会进入“不知所措”的状态,这个状态会block住整个事务。也就是说,协调者Coordinator对于事务的完成非常重要,Coordinator的可用性是个关键。 因些,我们引入三段提交,三段提交在Wikipedia上的描述如下,他把二段提交的第一个段break成了两段

3PC

三阶段提交(Three-phase commit),也叫三阶段提交协议(Three-phase commit protocol),是二阶段提交(2PC)的改进版本。除了引入超时机制之外,3PC把2PC的准备阶段再次一分为二,这样三阶段提交就有CanCommit、PreCommit、DoCommit三个阶段。图片来自Wikipedia:

3PC

三段提交的核心理念是:在询问的时候并不锁定资源,除非所有人都同意了,才开始锁资源。

理论上来说,如果第一阶段所有的结点返回成功,那么有理由相信成功提交的概率很大。这样一来,可以降低参与者Cohorts的状态未知的概率。也就是说,一旦参与者收到了PreCommit,意味他知道大家其实都同意修改了。这一点很重要。下面我们来看一下3PC的状态迁移图:(注意图中的虚线,那些F,T是Failuer或Timeout,其中的:状态含义是 q – Query,a – Abort,w – Wait,p – PreCommit,c – Commit)

3PC

下面逐步分析3PC的过程:

CanCommit阶段

  1. 协调者向所有参与者发出包含事务内容的CanCommit请求,询问是否可以提交事务,并等待所有参与者答复。
  2. 参与者收到CanCommit请求后,如果认为可以执行事务操作,则反馈YES并进入预备状态,否则反馈NO。

PreCommit阶段

  1. 所有参与者均反馈YES,即执行事务预提交。

    • 协调者向所有参与者发出PreCommit请求,进入准备阶段。
    • 参与者收到PreCommit请求后,执行事务操作,将Undo和Redo信息记入事务日志中(但不提交事务)。
    • 各参与者向协调者反馈Ack响应或No响应,并等待最终指令。
  2. 任何一个参与者反馈NO,或者等待超时后协调者尚无法收到所有参与者的反馈,即中断事务

    • 协调者向所有参与者发出abort请求。
    • 无论收到协调者发出的abort请求,或者在等待协调者请求过程中出现超时,参与者均会中断事务。

Do Commit阶段

  1. 所有参与者均反馈Ack响应,即执行真正的事务提交。

    • 如果协调者处于工作状态,则向所有参与者发出do Commit请求。
    • 参与者收到do Commit请求后,会正式执行事务提交,并释放整个事务期间占用的资源。
    • 各参与者向协调者反馈Ack完成的消息。
    • 协调者收到所有参与者反馈的Ack消息后,即完成事务提交。
  2. 任何一个参与者反馈NO,或者等待超时后协调者尚无法收到所有参与者的反馈,即中断事务。

    • 如果协调者处于工作状态,则向所有参与者发出do Commit请求。
    • 参与者使用阶段1中的Undo信息执行回滚操作,并释放整个事务期间占用的资源。
    • 各参与者向协调者反馈Ack完成的消息。
    • 协调者收到所有参与者反馈的Ack消息后,即完成事务中断。

注意:进入阶段三后,无论协调者出现问题,或者协调者与参与者网络出现问题,都会导致参与者无法接收到协调者发出的do Commit请求或abort请求。此时,参与者都会在等待超时之后,继续执行事务提交

3PC总结
  • 优点:降低了阻塞范围,在等待超时后协调者或参与者会中断事务。避免了协调者单点问题,阶段3中协调者出现问题时,参与者会继续提交事务。

  • 缺陷:脑裂问题依然存在,即在参与者收到PreCommit请求后等待最终指令,如果此时协调者无法与参与者正常通信,会导致参与者继续提交事务,造成数据不一致。

无论2PC或3PC,均无法彻底解决分布式一致性问题。解决一致性问题,唯有Paxos,所有其他一致性算法都是Paxos算法的不完整版。下篇介绍著名的Paxos协议。^_^

猜你喜欢

转载自blog.csdn.net/TheLudlows/article/details/81712973