前提条件
系统节点分为:其中一个节点被设置为协调者(co-ordinator),其他节点设置为参与者(cohort)。
假定在每个节点上都有一个使用write-ahead log的稳定数据存储节点,说白了可以打日志,而且日志在persistent 存储中 。如果要成功,那么在收到commit前没有节点崩溃,write-log的日志保证不丢失,并且协调者和任意参与者都可以互相通信。所以如果要成功的话,条件还是很严格的。
算法
分两个阶段
提交请求阶段(或者叫做投票阶段)
1.协调者发送一个query to commit消息给所有的cohorts,等待直到收到所有cohorts的回复。
2.cohorts在本地节点执行事务(之后协调者会要求提交这个事务),写本地的redo和undo日志
3.每一个cohorts,如果执行成功,回复一个agreement消息(假如cohorts同意执行commit);如果执行失败,回复一个abort消息。(两阶段提交协议)。
提交阶段(或者叫完成阶段)
成功
如果协调者接收到所有参与者发送回来的agreement消息:
1.协调者发送一个commit消息给所有的cohorts
2.每一个参与者完成commit操作,(两阶段提交协议)释放所有事务处理过程中使用的锁资源
3.每一个参与者回复一个acknowledgment给协调者
4.协调者在收到所有acknowledgment消息之后完成整个操作
失败
如果任何一个参与者在提交请求阶段回复abort消息给协调者:
1.协调者回复一个rollback消息给所有的cohorts
2.每一个参与者执行本地事务的undo操作(根据undo日志记录),并且释放事务执行过程中使用的资源和锁
3.每一个参与者给协调者回复acknowledgement消息(两阶段提交协议)。
4.协调者在接收到所有的参与者的acknowledgement消息之后执行事务undo操作
缺点
两阶段提交协议最大的缺点是:它是一个阻塞协议。当一个节点在等待回复消息时进入阻塞状态。其他需要这些资源的处理事务需要等待。如果协调者挂掉,cohorts将永远不能结束它们的事务,如下面的情况所述:
如果一个参与者发送agreement消息给协调者,它将进入阻塞状态直到收到回复的commit或者rollback 消息。如果这个时候协调者挂掉,并且不再恢复,这个cohort将一直阻塞(为什么这里不能使用等待超时的机制来abort掉这个事务呢?个人理解,如果一个参与者独自决定将这个未完成事务abort掉,可能导致全局的数据不一致,因为不知道其他节点是否执行了abort操作),(两阶段提交协议)除非它可以从其他cohort那里获得全局的commit/abort消息
当协调者发送query to commit消息之后,它将阻塞直到收到所有参与者的回复消息。如果这个时候,一个参与者挂掉,并且不再能恢复,协调者用下面的方法来解除阻塞:因为协调者是唯一一个决定提交或回滚的节点,(两阶段提交协议)所以可以使用超时机制来解决阻塞问题。如果协调者在一段时间之内没有收到来自cohort的消息,它将不再等待,直接向所有的cohort发送abort消息。这是这个协议的又一个缺点:它倾向于abort这样的case,而不是完成这个case
两阶段提交中的故障处理:超时和重发机制
首先说明(源自《数据库与事务处理》一书,为什么要这样设计?)
协调者对事务开始和提交消息进行强制写入到非易失性介质中;
参与者对准备消息和提交消息进行强制写入到非易失性介质中。。(两阶段提交协议)。
写入非易失性介质中的日志用于在节点崩溃之后能查找到崩溃时节点在事务中的状态。
由两阶段提交协议的工作原理可见,之所以能够在不丢失运行记录信息的情况下.从所有故障中迅速恢复,就是因为在执行过程中维护了事务日志,记录了执行恢复所需要的信息。
现在来分析当发生不同类型故障时,2Pc的行为 。
n
站点故障
n
丢失报文
网络分割
站点故障
(1)一参与者在把就绪记录写入运行记录以前出现故障。在这种情况下,协调者超时机制满期,它将采取撤消的决定。所有的参与者都撤销它们的子事务。当发生该故障的参与者恢复时,重启动过程简单地撤销该事务即可.不需要过问其他站点的情况
(2)一参与者在把就绪记录写入运行记录以后发生故障。在这种情况下,其他参与者的站点终止该事务(提交或撤消)。当故障站点恢复时,重启动过程不得不询问协调者或别的某个参与者关于该事务的结果(提交或撤消),然后执行相应的动作(提交或撤消)。这种情况下需要访问远程的恢复信息.
(3)协调者在把预备记录写入运行记录以后,而在写入global-commit或global-abort记录以前发生故障。这种情况下所有已经回答READY的参与者必须等待协调者恢复。协调者的重启动过程从头开始恢复提交协议,从预备记录(在运行记录中)读取参与者的标识,再次把PREPARE(预备)报发送给它们。每个就绪的参与者必须要识别出该新的PREPARE报文是前一个的重复报文。
(4)协调者在远行记录中写入global-commit或global-abort记录以后而在写入完成记录以前发生故障。这种情况下,协调者在重启动时必须再次给所有参与者发送其决定,未曾收到此命令的所有参与者不得不等待到协调者恢复为止。和以前一样,参与者不应因收到该命令报文两次而受到影响。
(5)协调者在运行记录中写入完成以后发生故障。这种情况下,该事物已经结束,在重启动时不需任何动作。
丢失报文
(1)来自一个参与者的回答报文(READY或ABORT)被丢失。在这种情况下,协调者的超时满期,整个事务被撤销。要注意,只由协调者来发现这种故障,而从协调者的观点来看,它完全好像是一参与者的故障。但是.从参与者的观点来看情况就不同了,该参与者并不认为自己有故障,因而不会执行重启动过程。
(2)丢失一个PREPARE报文。这种情况下该参与者仍停在等待状态。因为协调者并没有收到回答,所以其全局结果和前一种情况相同。
(3)丢失一命令报文(commit或abort)。采用图4.15的协议时,该参与者对此命令处于不肯定状态。在参与者中引入超时机制就可简单地消除这个问题;从回答起在超时后仍末收到任何报文的话,就发送—请求再发送该命令。
(4)丢失一个AcK报文。协调者对参与者有无收到该报文处于不肯定状态。可以在协调者中引入超时机制就可简单地消除这个问题;如果从发出命令起到超时后仍未受到任何AcK报文,协调者就再次发送该命令。在参与者站点处理这种情况的最好办法是再次发送AcK报文,即使该子事务在那期间已经完成并不再活动也要重发。
网络分割
这里假设发生了简单的网络分割情况,即把站点分成为两个组;包含协调者的组叫做协调者组,而另一组叫参与者组。从协调者的观点来看,这种分割等效于一组参与者的故障情况,与上述第1(1)和1(2)点相似:协调者作出决定然后把命令发送给协调者一组中的所有参与者,因而这些站点能够正确地结束事务。如从参与者组的成员观点来看,这种分割等效于协调者故障,情况与上述第1(3)和1(4)点相似。
要注意,对于涉及处理分布式事务的站点来说,其恢复过程要比集中式数据库复杂。在集中式数据库中,只合两种可能;事务要么提交,要么不提交,所以恢复机构执行相应的重做或撤销动作。在分布式数据库中,还可能有其他情况
(1)一个参与者就绪(情况1(2))。
(2)协调者已启动第1阶段(情况1(3))。
(3)协调者已启动第2阶段(情况1(4))。
这些情况分布式数据库管理系统中的恢复机制都能识别,并根据识别的情况作相应的处理。
其他:
1.协调者在等待投票消息时超时。协调者发送abort消息给所有参与者,终止事务
2
.协调者在等待提交完毕消息时超时。协调者与参与者联系,确认参与者的提交完毕消息。如果协调者无法联系上这个cohort,无法知道它是否正常提交,则放弃,因为已提交完成的参与者无法做回滚操作了。
这时该怎么处理呢?因为其他参与者已经完成提交,不能对事务进行回滚。可以这样考虑:协调者在多次重试都无法得到完成提交的消息之后可以放弃,待参与者重新恢复自行处理,这时可能有两种情况:a.参与者在本地日志中发现已完成本地提交,所以可能由于网络故障导致提交完成消息没有到达协调者,所以直接忽略;b.参与者发现在本地日志中发现尚未提交成功,因为到达这里,可以肯定本地已做好提交准备,但是不知道协调者是决定提交,所以向协调者询问,按协调者的回复来进行提交或回滚)。(两阶段提交协议)。
3.协调者在发送准备到发送提交消息的这段时间中崩溃。协调者恢复重启后,发现并未做提交操作,保险操作(因为协调者不知道它是否发现欧诺个准备消息,或其他参与者是否做好提交准备),直接发送abort消息给所有参与者,终止事务
4.协调者在发送提交消息之后崩溃。这种情况下,不能保证所有参与者都已收到了提交消息,所以给所有的参与者发送commit消息,保证事务的正常提交
5.参与者在等待commit或abort消息的时候崩溃。重启之后发现日志中有事务准备消息,尝试向协调者询问事务状态,根据回复做提交或异常终止。如果无法联系上协调者,则向其他cohorts询问事务状态,如果有某一个节点已经做了提交或异常终止(说明协调者已发送了相关消息),则做同样的操作
6.参与者在收到commit消息,完成提交之后出现崩溃。这时可能协调者在等待该参与者的提交完成回应消息,所以参与者主动联系协调者告知事务状态。
个人总结:
两阶段提交协议其实是将集中式的提交协议(常用单机数据库的事务提交方法)的工程拆开成两个阶段,从commit这个步骤将其拆开,在commit之前,(两阶段提交协议)发送消息给协调者,等待协调者确认收集到所有的可提交消息之后,再执行提交操作。当然最后还需要参与者回复ack消息给协调者确认本地事务提交已成功