拜占庭將軍問題和FLP的啟示

Byzantine Generals Problem

昨天那篇文章提到的Two General Paradox實(shí)際上是一個(gè)弱化的Byzantine Generals Problem. 1982年, Leslie Lamport和另外兩位科學(xué)家年發(fā)表了一篇論文闡述 [The Byzantine Generals Problem, ACM Transactions on Programming Languages and Systems,Volume 4 Issue 3, July 1982 Pages 382-401] 描述了更一般化的情況, 這種情況下不僅是網(wǎng)絡(luò)會(huì)有故障, 節(jié)點(diǎn)本身也會(huì)有不按照邏輯執(zhí)行的問題. 比如一個(gè)叛徒將軍亂發(fā)消息或者不按照程序邏輯執(zhí)行. 完整的拜占庭將軍問題更加復(fù)雜, 必須加以特定場景的假設(shè)才能解決, 比如同步網(wǎng)絡(luò). 這個(gè)問題比較復(fù)雜, 本文只是簡要介紹一下.

在同步網(wǎng)絡(luò)中, 有3f+1個(gè)節(jié)點(diǎn), 如果故障節(jié)點(diǎn)不超過f個(gè), 那么這個(gè)問題是可以解決的. 我們這里不做嚴(yán)格證明, 但是可以簡單解釋一下原因。

我們考慮最基礎(chǔ)的一種情況, 假設(shè)有三個(gè)將軍, 只有一個(gè)叛徒. 如果A是叛徒, 那么A可能會(huì)給B發(fā)出進(jìn)攻(1), 然后給C發(fā)出撤退(0)的命令. 當(dāng)B和C互相同步信息的時(shí)候他們會(huì)發(fā)現(xiàn)兩個(gè)不一致的信息. 但是B和C誰也無法判斷誰是叛徒, 比如從B的角度來看, 他無法判斷A是叛徒或者C是叛徒. 所以三個(gè)將軍里有一個(gè)叛徒是無法解決的. 如果消息可以防止偽造, 那么在同步網(wǎng)絡(luò)中叛徒達(dá)到1/3也是可以解決的. 下圖右邊從C的角度來看, 因?yàn)橄⑹钦鎸?shí)無法偽造的, 那么很明顯A是叛徒. 由此可以推導(dǎo)到3f+1的情況.

但是同步網(wǎng)絡(luò)現(xiàn)實(shí)生活中太少, 如果要考慮在異步網(wǎng)絡(luò)之中, 拜占庭將軍問題是非常難解決的, 實(shí)際上根據(jù)FLP定理, 異步網(wǎng)絡(luò)中是沒有完全同時(shí)保證safety和liveness的一致性算法的. 但是在實(shí)際工程中我們?nèi)绻潘蒷iveness的要求, 是有實(shí)際可用的算法的, 這個(gè)算法進(jìn)入無限循環(huán)的概率非常非常低. 1999年Miguel Castro和Barbara Liskov提出了PBFT算法(Practical Byzantine Fault Tolerance), 這個(gè)算法可以在異步網(wǎng)絡(luò)中不保證liveness的情況下解決拜占庭將軍問題. 雖然不保證Liveness但是這個(gè)算法進(jìn)入無限循環(huán)的概率非常低, 在工程中是完全可用的. 實(shí)際上他們實(shí)現(xiàn)了一個(gè)PBFT的分布式NFS文件服務(wù)器, 最壞的時(shí)候性能只下降了24%! 為此BarbaraLiskov獲得了2008年代圖靈獎(jiǎng). 有興趣的同學(xué)可以自己去看 Practical Byzantine Fault Tolerance and Proactive Recovery. (ACM Transactions on Computer Systems, Vol. 20, No. 4, November 2002, Pages 398–461).

順便提一句, 面向?qū)ο笾械腖iskov替換原則也是她提出的.

圖片來源:維基百科, Barbara Liskov 2010


FLP Impossibility

分布式事務(wù)作為Consensus類型問題, 在異步網(wǎng)絡(luò)中非常難實(shí)現(xiàn). 很多科學(xué)家們做了很多偉大的嘗試, 包括2PC, 3PC, 等等, 但是1985年的時(shí)候, 一個(gè)重要的論文告訴了我們答案, 這就是著名的FLP Impossibility:

No completely asynchronous consensus protocol can tolerate even a single unannounced process death. [ Impossibility of Distributed Consensus with One Faulty Process,Journal of the Association for Computing Machinery, Vol. 32, No. 2, April 1985]

在異步網(wǎng)絡(luò)環(huán)境中只要有一個(gè)故障節(jié)點(diǎn), 任何Consensus算法都無法保證正確結(jié)束. (這里unannounced process death是指一個(gè)進(jìn)程停止工作了但是其它節(jié)點(diǎn)不知道, 其它節(jié)點(diǎn)認(rèn)為是消息延遲或者這個(gè)進(jìn)程特別慢. FLP假設(shè)沒有拜占庭的故障節(jié)點(diǎn), 那種情況過于困難, 故障在這里的定義是指進(jìn)程停止嘗試讀取消息, 相當(dāng)于crash-stop).

FLP中設(shè)計(jì)的模型是一個(gè)比現(xiàn)實(shí)情況要更可靠的模型, 當(dāng)然了, 如果連更可靠的模型下一致性問題失效那么現(xiàn)實(shí)中更寬松的環(huán)境當(dāng)然也是失效的. FLP還假設(shè)異步網(wǎng)絡(luò)是可靠的, 盡管有延遲但是所有的消息都會(huì)投遞一次且僅一次, 每個(gè)進(jìn)程只會(huì)寫入一次狀態(tài), 然后就進(jìn)入了decision state, 這是一個(gè)很強(qiáng)的保證, 幾乎沒有任何網(wǎng)絡(luò)能達(dá)到這樣的可靠性. FLP并不要求所有非故障節(jié)點(diǎn)都達(dá)成一致, 只要有一個(gè)進(jìn)程進(jìn)入decision state就算達(dá)成一致了, 而且一致結(jié)果只能是屬于{0, 1}, 這也是非?!比菀住钡腶greement約束. 加上前面還提到最多只有一個(gè)進(jìn)程發(fā)生故障, 相對(duì)于現(xiàn)實(shí)情況這已經(jīng)是一個(gè)極端可靠的環(huán)境了, 但是在這樣可靠的環(huán)境中仍然無法有一個(gè)一致性算法存在! 更不用說真實(shí)世界中的網(wǎng)絡(luò)分區(qū)問題和拜占庭式問題了. FLP的證明告訴我們一致性算法的liveness是無法保證的, 如果你要safety那么就會(huì)可能進(jìn)入無限循環(huán), 每次狀態(tài)變化都會(huì)可能保持當(dāng)前的狀態(tài)是可分支的(bivalent). FLP的嚴(yán)格證明很有趣, 但限于篇幅這里就不做介紹了. 有興趣的同學(xué)可以去看原論文, 如果你覺得里面的數(shù)學(xué)語言比較晦澀, 可以看我寫的一篇白話文的證明過程描述(鏈接:http://danielw.cn/FLP-proof/).

圖片來源:MIT, Nancy Lynch, 兩次Dijkstra獎(jiǎng)獲得者.

所有consensus問題最終在異步網(wǎng)絡(luò)上只要有一個(gè)故障節(jié)點(diǎn)就都無法達(dá)成完全一致并結(jié)束. 這個(gè)理論的證明非常重要, 它終止了多年的爭論, 現(xiàn)在你可以省省力氣了, 不要再浪費(fèi)精力去試圖設(shè)計(jì)一個(gè)能在異步網(wǎng)絡(luò)上能夠容忍各種故障并保持一致的系統(tǒng)了. 比如分布式事務(wù)是永遠(yuǎn)無法實(shí)現(xiàn)單體應(yīng)用級(jí)別的一致性的.

無論是Paxos還是Raft算法, 理論上都可能會(huì)進(jìn)入無法表決通過的死循環(huán)(但是這個(gè)概率其實(shí)是非常非常低的), 但是他們都是滿足safety的, 只是放松了liveness的要求, Barbara Liskov的PBFT也是這樣. 在實(shí)際應(yīng)用中, 上下游系統(tǒng)之間的一致性除了通過2PC或者Paxos Commit來實(shí)現(xiàn), 我們也可以通過其他方式來實(shí)現(xiàn)最終的一致, 來避免2PC的缺點(diǎn). 現(xiàn)實(shí)中你不得不接受短時(shí)間的不一致在分布式系統(tǒng)中是一種常態(tài)的事實(shí). CAP理論的提出者Eric Brewer曾經(jīng)這樣說過:

So the general answer is you allow things to be inconsistent and thenyou find ways to compensate for mistakes, versus trying to prevent mistakes altogether. In fact, the financial system is actually not based on consistency, it's based on auditing and compensation. They didn't know any thing about the CAP theorem, that was just the decision they made in figuring out what they wanted, and that's actually, I think, the right decision.

舉個(gè)例子,實(shí)際上你從A銀行往B銀行轉(zhuǎn)賬實(shí)際上是沒有兩階段提交或者分布式事務(wù)的, 金融行業(yè)是一個(gè)古老的行業(yè), 在計(jì)算機(jī)出現(xiàn)之前銀行就已經(jīng)出現(xiàn)了, 金融行業(yè)有一點(diǎn)非常值得我們借鑒, 那就是WORM(write once read many). 金融行業(yè)把不一致當(dāng)做常態(tài)而非異常.舉個(gè)例子, 會(huì)計(jì)記賬的時(shí)候, 如果發(fā)現(xiàn)前面有一筆預(yù)付記錄或者錯(cuò)誤記錄, 會(huì)計(jì)不會(huì)用橡皮抹掉那條記錄再改成正確的, 會(huì)計(jì)只會(huì)在最后面加一筆記錄來抵消前面的錯(cuò)誤, 或者按照差額加一筆抵沖的記錄. 任何記錄只能寫入一次, 然后再也不會(huì)改變. 我們假設(shè)沒有人民銀行作為中間節(jié)點(diǎn), 把轉(zhuǎn)賬簡化為A銀行直接給B銀行轉(zhuǎn)賬, 在轉(zhuǎn)賬過程中如果A已經(jīng)扣款并通知了B,但是B發(fā)現(xiàn)這個(gè)賬號(hào)由于洗錢被鎖定了, 不能入款, 那么B返回拒絕消息給A, 這時(shí)候A可以再追加一筆補(bǔ)償交易, 把剛才扣掉的錢補(bǔ)償回來. 整個(gè)過程可能是幾秒鐘, 也可能是幾分鐘, 也有可能是第二天(比如網(wǎng)絡(luò)故障, 重試多次后放棄, 對(duì)賬時(shí)發(fā)現(xiàn)). 在任何一個(gè)步驟發(fā)生故障, 用戶都會(huì)經(jīng)歷一定時(shí)間的不一致, 從前面的討論我們知道2PC如果允許超時(shí)回滾, 2PC也無法消除這個(gè)不一致的時(shí)間窗口, 但是只要有歷史記錄我們就可以通過自動(dòng)補(bǔ)償或者每日對(duì)賬去補(bǔ)償, 讓數(shù)據(jù)重新一致. 這種事務(wù)可以很好地忍耐各種故障, 包括網(wǎng)絡(luò)分區(qū), 只要每個(gè)消息都有全局唯一的id或者消息是冪等的即可. 當(dāng)網(wǎng)絡(luò)恢復(fù)時(shí)任何一個(gè)節(jié)點(diǎn)都可以根據(jù)消息id輕松地去掉重復(fù)的消息, 當(dāng)消息丟失時(shí), 可以稍后重試或者每天對(duì)賬.

如果事務(wù)包含特別復(fù)雜的計(jì)算, 或者涉及了線下的物流或者多方交易, 那么這樣的事務(wù)可能需要幾天才能完成, 這種長事務(wù)(Long Lived Transaction) 在2PC中更是無法處理, 我們總不能去鎖定這些數(shù)據(jù)幾天吧? 1987年普林斯頓大學(xué)的Garcia-Molina和Salem發(fā)表了一篇論文提出了saga的概念. 一個(gè)長事務(wù)T中的操作可以拆分為彼此獨(dú)立的本地事務(wù)T1, T2, T3, 那么就可以稱之為saga. 其中的每一個(gè)Ti都有一個(gè)相應(yīng)的補(bǔ)償事務(wù)Ci. 如果部分Ti失敗了需要Ci來修復(fù)回原始狀態(tài),Ci不會(huì)直接把數(shù)據(jù)庫改回原先的狀態(tài), Ci通常是像前面說的會(huì)計(jì)的做法追加修正內(nèi)容去抹平Ti帶來的變化. 下圖中事務(wù)參與者有A,B, C, 每次發(fā)起的事務(wù)都有一個(gè)全局唯一的id, 參與者之間的消息在網(wǎng)絡(luò)故障時(shí)可以重發(fā) (可以通過id去重復(fù)消息, 或者保證冪等操作). 假如T3在C中執(zhí)行時(shí)失敗了, 如果T3已經(jīng)提交了或者這個(gè)系統(tǒng)不支持回滾, 那么必須使用C3來補(bǔ)償T3帶來的變化. 然后發(fā)消息給B, B會(huì)執(zhí)行C2去補(bǔ)償T2, 然后B可以選擇繼續(xù)通知A去回滾, 或者稍后等外部條件發(fā)生變化再執(zhí)行T2, 然后讓C去重試. 這樣整個(gè)系統(tǒng)變成了一個(gè)狀態(tài)機(jī),參與者之間雖然有可能不一致,一個(gè)訂單或者一筆交易一定會(huì)處于狀態(tài)機(jī)的某一個(gè)狀態(tài), 整個(gè)事務(wù)的過程仍然是整體可以追溯的.

Saga這種方法并不適合所有的情況, 它也不是銀彈, 但是它是比2PC/3PC更適合來解決分布式事務(wù). 2PC/3PC的思路是想在源頭上阻止分布式事務(wù)不一致的產(chǎn)生, 但這是不可能實(shí)現(xiàn)的. 不一致是常態(tài), 不是異常, 異步網(wǎng)絡(luò)中能容忍節(jié)點(diǎn)故障的total correct的consensus算法不存在。

本文作者:Daniel吳強(qiáng)(點(diǎn)融黑幫),現(xiàn)任點(diǎn)融網(wǎng)首席社交平臺(tái)架構(gòu)師,前盛大架構(gòu)師, 專注分布式系統(tǒng)和移動(dòng)應(yīng)用, 有十三年的開發(fā)經(jīng)驗(yàn), 目前在點(diǎn)融做最愛的兩件事情: 寫代碼和重構(gòu)代碼。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 分布式系統(tǒng)面臨的第一個(gè)問題就是數(shù)據(jù)分布,即將數(shù)據(jù)均勻地分布到多個(gè)存儲(chǔ)節(jié)點(diǎn)。另外,為了保證可靠性和可用性,需要將數(shù)據(jù)...
    olostin閱讀 4,922評(píng)論 2 26
  • 拜占庭將軍問題很多人可能聽過,但不知道是什么意思,本文從非專業(yè)的角度來講講,拜占庭將軍問題到底是說什么的。 拜占庭...
    蘇江同學(xué)閱讀 40,204評(píng)論 29 68
  • 昨天我們介紹了拜占庭將軍問題和FLP Impossibility, 我們知道了在異步網(wǎng)絡(luò)中的total corre...
    點(diǎn)融黑幫閱讀 1,690評(píng)論 0 6
  • 想你
    蓋世英雄hero閱讀 290評(píng)論 0 0
  • 1.冒泡排序2.選擇排序3.插入排序4.快速排序5.堆排序6.希爾排序7.歸并排序8.計(jì)數(shù)排序9.桶排序10.基數(shù)...
    IT楓閱讀 697評(píng)論 0 5

友情鏈接更多精彩內(nèi)容