Two-phase commit(http://en.wikipedia.org/wiki/Two-phase_commit_protocol)是分布式事務最基礎的協(xié)議,Three-phase commit(http://en.wikipedia.org/wiki/Three-phase_commit_protocol)主要解決Two-phase commit中協(xié)調者宕機問題。
Two-phase commit的算法實現(xiàn) (from <<Distributed System: Principles and Paradigms>>):
協(xié)調者(Coordinator):
write START_2PC to local log;
multicast VOTE_REQUEST to all participants;
while not all votes have been collected {
wait for any incoming vote;
if timeout {
write GLOBAL_ABORT to local log;
multicast GLOBAL_ABORT to all participants;
exit;
}
record vote;
}
if all participants sent VOTE_COMMIT and coordinator votes 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;
}
參與者(Participants)
write INIT to local log;
wait for VOTE_REQUEST from coordinator;
if timeout {
write VOTE_ABORT to local log;
exit;
}
if participant votes COMMIT {
write VOTE_COMMIT to local log;
send VOTE_COMMIT to coordinator;
wait for DECISION from coordinator;
if timeout {
multicast DECISION_REQUEST to other participants;
wait until 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 VOTE_ABORT to local log;
send VOTE_ABORT to coordinator;
}
另外,每個參與者維護一個線程專門處理其它參與者的DECISION_REQUEST請求,處理線程流程如下:
while true {
wait until any incoming DECISION_REQUEST is received;
read most recently recorded STATE from the local log;
if STATE == GLOBAL_COMMIT
send GLOBAL_COMMIT to requesting participant;
else if STATE == INIT or STATE == GLOBAL_ABORT;
send GLOBAL_ABORT to requesting participant;
else
skip; /* participant remains blocked */
}
從上述的協(xié)調者與參與者的流程可以看出,如果所有參與者VOTE_COMMIT后協(xié)調者宕機,這個時候每個參與者都無法單獨決定全局事務的最終結果(GLOBAL_COMMIT還是GLOBAL_ABORT),也無法從其它參與者獲取,整個事務一直阻塞到協(xié)調者恢復;如果協(xié)調者出現(xiàn)類似磁盤壞這種永久性錯誤,該事務將成為被永久遺棄的孤兒。問題的解決有如下思路:
1. 協(xié)調者持久化數(shù)據(jù)定期備份。為了防止協(xié)調者出現(xiàn)永久性錯誤,這是一種代價最小的解決方法,不容易引入bug,但是事務被阻塞的時間可能特別長,比較適合銀行這種正確性高于一切的系統(tǒng)。
2. Three-phase Commit。這是理論上的一種方法,實現(xiàn)起來復雜且效率低。思路如下:假設參與者機器不可能出現(xiàn)超過一半同時宕機的情況,如果協(xié)調者宕機,我們需要從活著的超過一半的參與者中得出事務的全局結果。由于不可能知道已經(jīng)宕機的參與者的狀態(tài),所以引入一個新的參與者狀態(tài)PRECOMMIT,參與者成功執(zhí)行一個事務需要經(jīng)過INIT, READY, PRECOMMIT,最后到COMMIT狀態(tài);如果至少有一個參與者處于PRECOMMIT或者COMMIT,事務成功;如果至少一個參與者處于INIT或者ABORT,事務失敗;如果所有的參與者都處于READY(至少一半?yún)⑴c者活著),事務失敗,即使原先宕機的參與者恢復后處于PRECOMMIT狀態(tài),也會因為有其它參與者處于ABORT狀態(tài)而回滾。PRECOMMIT狀態(tài)的引入給了宕機的參與者回滾機會,所以Three-phase commit在超過一半的參與者活著的時候是不阻塞的。不過,Three-phase Commit只能算是是理論上的探索,效率低并且沒有解決網(wǎng)絡分區(qū)問題。
3. Paxos解決協(xié)調者單點問題。Jim Gray和Lamport合作了一篇論文講這個方法,很適合互聯(lián)網(wǎng)公司的超大規(guī)模集群,Google的Megastore事務就是這樣實現(xiàn)的,不過問題在于Paxos和Two-phase Commit都不簡單,需要有比較靠譜(代碼質量高)的小團隊設計和編碼才行。后續(xù)的blog將詳細闡述該方法。
總之,分布式事務只能是系統(tǒng)開發(fā)者的烏托邦式理想,Two-phase commit的介入將導致涉及多臺機器的事務之間完全串行,沒有代價的分布式事務是不存在的。