两阶段提交的协议大家都比较熟悉了,解释一下每个阶段的异常处理。首先,我们需要持久化协议过程中的状态,这样如果server宕机,那么恢复的时候还能通过日志知道宕机前处于那个阶段。同时,所有对数据的修改都会先写write ahead log,保证宕机重启的之后数据也不会丢失。写日志的顺序假定为:写write ahead log-修改缓冲区-写commit/abort log。
在这个前提下,我们根据如下的时序图来讨论异常情况和处理方法。
- 过程a没有成功,即协调者没有收到部分参与者的回应。超时后,协调者发送abort消息给参与者取消事务。参与者存在两种情况:
- 过程b没有成功,即协调者发送commit消息之后没有收到部分参与者的回应。协调者需要重试,确认参与者的提交完毕消息,如果多次尝试不能联系上,则等待参与者上线之后解决。参与者存在两种情况:
- 过程c没有成功,即参与者发送vote回应消息之后没有等到协调者的commit/rollback消息。这个过程参与者的异常处理已经讨论过了,这里讨论协调者的异常处理。存在两种情况:
算法的伪代码可以参考如下代码,摘自《Distributed Systems: Principles and Paradigms》。
Actions of Coordinator
write("START_2PC tolocal log");
multicast("VOTE_REQUESTto all participants");
while(not all votes have been collected)
{
waitfor("any incoming vote");
if(timeout)
{
write("GLOBAL_ABORT to local host");
multicast("GLOBAL_ABORT to all participants");
exit();
}
record(vote);
}
if(all participants send VOTE_COMMIT and coordinatorvotes COMMIT)
{
write("GLOBAL_COMMIT to local log");
multicast("GLOBAL_COMMIT to all participants");
}
else
{
write("GLOBAL_ABORT to local log");
multicast("GLOBAL_ABORT to all participants");
}
**Actions of Participants**
write("INIT to locallog");
waitfor("VOTE_REQUESTfrom coordinator");
if(timeout)
{
write("VOTE_ABORT to local log");
exit();
}
if("participantvotes COMMIT")
{
write("VOTE_COMMIT to local log");
send("VOTE_COMMIT to coordinator");
waitfor("DESCISION from coordinator");
if(timeout)
{
multicast("DECISION_REQUEST to other participants");
waituntil("DECISION is received"); /// remain blocked
write("DECISION to local log");
}
if(DECISION == "GLOBAL_COMMIT")
{
write("GLOBAL_COMMIT to local log");
}
else if(DECISION== "GLOBAL_ABORT")
{
write("GLOBAL_ABORT to local log");
}
}
else
{
write("GLOBAL_ABORT to local log");
send("GLOBAL_ABORT to coordinator");
}
参考文献:
[1] 两阶段提交(2PC)协议, http://blog.chinaunix.net/uid-20761674-id-75164.html
PREVIOUS发一张喵星人的照片