转载:http://blog.chinaunix.net/uid-20761674-id-75164.html
部分翻译自http://en.wikipedia.org/wiki/Two-phase_commit_protocol
两阶段提交协议是在事务处理、数据库,以及计算机网络中使用的一种原子提交协议(atomic commitment)。它是一个分布式算法,协调在整个分布式原子事务中的参与者的行为(commit或者roll back)。这个协议在一些系统错误发生时仍然能够成功,((两阶段提交协议))但并不能保证对所有错误都能进行容错。为了能做错误恢复,协议的所有参与者都需要使用日志对协议状态进行记录。
前提条件 (两阶段提交协议)。
两阶段提交协议在下面的条件下执行:其中一个节点被设置为coordinator,其他节点设置为cohorts。协议假定在每个节点上都有一个使用write-ahead log的稳定数据存储节点。没有节点崩溃,write-log的日志保证不丢失(这两点假设太过了吧,节点崩溃之后可以做恢复吧?)。并且任意两个节点都可以互相通信(这个条件过于严格)。
算法
提交请求阶段(或者叫做投票阶段)
1.coordinator发送一个query to commit消息给所有的cohorts,等待直到收到所有cohorts的回复。
2.cohorts在本地节点执行事务(之后coordinator会要求提交这个事务),写本地的redo和undo日志
3.每一个cohorts,如果执行成功,回复一个agreement消息(假如cohorts同意执行commit);如果执行失败,回复一个abort消息。(两阶段提交协议)。
提交阶段(或者叫完成阶段)
成功
成功
如果coordinator接收到所有cohorts发送回来的agreement消息:
1.coordinator发送一个commit消息给所有的cohorts
2.每一个cohorts完成commit操作,(两阶段提交协议)释放所有事务处理过程中使用的锁资源
3.每一个cohorts回复一个acknowledgment给coordinator
4.coordinator在收到所有acknowledgment消息之后完成整个操作
失败
如果任何一个cohorts在提交请求阶段回复abort消息给coordinator:
1.coordinator回复一个rollback消息给所有的cohorts
2.每一个cohorts执行本地事务的undo操作(根据undo日志记录),并且释放事务执行过程中使用的资源和锁
3.每一个cohorts给coordinator回复acknowledgement消息(两阶段提交协议)。
4.coordinator在接收到所有的cohorts的acknowledgement消息之后执行事务undo操作
缺点
两阶段提交协议最大的缺点是:它是一个阻塞协议。当一个节点在等待回复消息时进入阻塞状态。其他需要这些资源的处理事务需要等待。如果coordinator挂掉,cohorts将永远不能结束它们的事务,如下面的情况所述:
如果一个cohort发送agreement消息给coordinator,它将进入阻塞状态直到收到回复的commit或者rollback 消息。如果这个时候coordinator挂掉,并且不再恢复,这个cohort将一直阻塞(为什么这里不能使用等待超时的机制来abort掉这个事务呢?个人理解,如果一个cohort独自决定将这个未完成事务abort掉,可能导致全局的数据不一致,因为不知道其他节点是否执行了abort操作),(两阶段提交协议)除非它可以从其他cohort那里获得全局的commit/abort消息
当coordinator发送query to commit消息之后,它将阻塞直到收到所有cohorts的回复消息。如果这个时候,一个cohort挂掉,并且不再能恢复,coordinator用下面的方法来解除阻塞:因为coordinator是唯一一个决定提交或回滚的节点,(两阶段提交协议)所以可以使用超时机制来解决阻塞问题。如果coordinator在一段时间之内没有收到来自cohort的消息,它将不再等待,直接向所有的cohort发送abort消息。这是这个协议的又一个缺点:它倾向于abort这样的case,而不是完成这个case
两阶段提交中的故障处理:
首先说明(源自《数据库与事务处理》一书,为什么要这样设计?)
coordinator对事务开始和提交消息进行强制写入到非易失性介质中;
cohorts对准备消息和提交消息进行强制写入到非易失性介质中。。(两阶段提交协议)。
写入非易失性介质中的日志用于在节点崩溃之后能查找到崩溃时节点在事务中的状态。
如果对以上日志操作的写入,首先在缓存中写入,(两阶段提交协议)然后批量写入非易失性介质,那么情况会更加复杂,因为如果节点崩溃,会丢失当前的事务状态。
1.coordinator在等待投票消息时超时。coordinator发送abort消息给所有cohorts,终止事务
2.coordinator在等待提交完毕消息时超时。coordinator与cohort联系,确认cohort的提交完毕消息。如果coordinator无法联系上这个cohort,无法知道它是否正常提交,则放弃,因为已提交完成的cohorts无法做回滚操作了。
这时该怎么处理呢?因为其他cohorts已经完成提交,不能对事务进行回滚。可以这样考虑:coordinator在多次重试都无法得到完成提交的消息之后可以放弃,待cohort重新恢复自行处理,这时可能有两种情况:a.cohort在本地日志中发现已完成本地提交,所以可能由于网络故障导致提交完成消息没有到达coordinator,所以直接忽略;b.cohort发现在本地日志中发现尚未提交成功,因为到达这里,可以肯定本地已做好提交准备,但是不知道coordinator是决定提交,所以向coordinator询问,按coordinator的回复来进行提交或回滚)。(两阶段提交协议)。
3.coordinator在发送准备到发送提交消息的这段时间中崩溃。coordinator恢复重启后,发现并未做提交操作,保险操作(因为coordinator不知道它是否发现欧诺个准备消息,或其他cohorts是否做好提交准备),直接发送abort消息给所有cohorts,终止事务
4.coordinator在发送提交消息之后崩溃。这种情况下,不能保证所有cohorts都已收到了提交消息,所以给所有的cohorts发送commit消息,保证事务的正常提交
5.cohorts在等待commit或abort消息的时候崩溃。重启之后发现日志中有事务准备消息,尝试向coordinator询问事务状态,根据回复做提交或异常终止。如果无法联系上coordinator,则向其他cohorts询问事务状态,如果有某一个节点已经做了提交或异常终止(说明coordinator已发送了相关消息),则做同样的操作
6.cohort在收到commit消息,完成提交之后出现崩溃。这时可能coordinator在等待该cohort的提交完成回应消息,所以cohort主动联系coordinator告知事务状态。
个人总结:
两阶段提交协议其实是将集中式的提交协议(常用单机数据库的事务提交方法)的工程拆开成两个阶段,从commit这个步骤将其拆开,在commit之前,(两阶段提交协议)发送消息给coordinator,等待coordinator确认收集到所有的可提交消息之后,再执行提交操作。当然最后还需要cohort回复ack消息给coordinator确认本地事务提交已成功
问题:
1.在提交阶段,cohorts收到commit消息之后,所有cohorts都应该执行commit操作,这时如果有某个cohorts执行commit失败,那么coordinator将不会收到这个corhort的ack消息,这是coordinator将如何处理,因为其他正常提交的cohorts已经在本地完成了commit操作,本地数据还能作rollback操作吗?如果不能,会出现数据不一致的情况。(两阶段提交协议)。
其他资源:
See also
- Atomic commit
- Commit (data management)
- Three-phase commit protocol
- XA
- Paxos commit, an alternative fault-tolerant commit algorithm based on the Paxos algorithm for n-process consensus.