分佈式系統漫談【玖】_分佈式事務一致性:協議支持

 

上篇文章:分佈式系統漫談【捌】_分佈式事務一致性:理論基礎

 

上文介紹了關於分佈式事務的理論基礎,下面我們總結下幾個分佈式事務一致性協議。

 

 

兩階段提交協議


兩階段提交協議(2PC)把分佈式事務分爲兩個階段,一個是準備階段,另一個是提交階段。準備階段和提交階段都是由事務管理器發起的。事務管理器是兩階段提交引入的一個第三方角色,它本身不參加事務的提交,只是作爲一個協調者存在於事務處理流程中。兩階段提交的流程可總結如下:

 

準備階段:協調者向參與者發起指令,參與者評估自己的狀態,如果參與者一評估指令可以完成,則會寫redo或者undo日誌(Write-Ahead Log的一種,然後鎖定資源,執行操作,但是並不提交。

 

提交階段:如果每個參與者明確返回準備成功,也就是預留資源和執行操作成功,則協調者向參與者發起提交指令,參與者提交資源變更的事務,釋放鎖定的資源:如果了任何一個參與者明確返回準備失敗,也就是預留資源或者執行操作失敗,則協調者向參與者發起中止指令,參與者取消已經變更的事務,執行undo日誌,釋放鎖定的資源。


 

兩階段提交存在的問題


阻塞:對於任何一次指令都必須收到明確的響應,纔會繼續進行下一步,否則處於阻塞狀態,佔用的資源被一直鎖定,不會被釋放。

 

單點故障:如果協調者宕機,參與者沒有協調者指揮,則會一直阻塞,儘管可以通過選舉新的協調者件代原有協調者,但是如果協調者在發送一個提交指令後宕機,而提交指令僅僅被一個參與者接收,並且參與者接收後也宕機,則新上任的協調者無法處理這種情祝。

 

腦裂:協調者發送提交指令,有的參與者接收到並執行了事務,有的參與者沒有接收到事務就沒有執行事務,多個參與者之間是不一致的。

 

爲了解決這些問題,後面又提出了三階段提交協議。

 

三階段提交協議

 

三階段提交協議是兩階段提交協議的改進版本。它通過超時機制解決了阻塞的問題,並且把兩個階段增加爲以下三個階段。

 

詢問階段:協調者詢問參與者是否可以完成指令,協調者只需要回答是或不是,而不需要做真正的操作,這個階段超時會導致中止。

 

準備階段:如果在詢問階段所有參與者都返回可以執行操作,則協調者向參與者發送預執行請求,然後參與者寫redo和undo日誌,執行操作但是不提交操作;如果在詢問階段任意參與者返回不能執行操作的結果,則協調者向參與者發送終止請求,這裏的邏輯與兩階段提交協議的準備階段是相似的。

 

提交階段:如果每個參與者在準備階段返回準備成功,也就是說預留資源和執行操作成功,則協調者向參與者發起提交指令,參與者提交資源變更的事務,釋放鎖定的資源;如果任何參與者返回準備失敗,也就是說預留資源或者執行操作失敗,則協調者向參與者發起中止指令,參與者取消已經變更的事務,執行Undo日誌,釋放鎖定的資源,這裏的邏輯與兩階段提交協議的提交階段一致。

 

兩階段提交和三階段提交的區別


1.增加了1個詢問階段,詢問階段可以確保儘可能早地發現無法執行操作而需要中止的行爲,但是它並不能發現所有這種行爲,只會減少這種情況的發生;

 

2.在準備階段以後,協調者和參與者執行的任務中都增加了超時,一以超時,則協調者和參與者都會繼續提交事務,默認爲成功,這也是根據概率統計超時後默認爲成功的正確性最大。
 

 

兩階段及三階段方案中都包含多個參與者、多個階段實現一個事務,實現複雜,性能也是個很大的問題,因此,在互聯網的高併發系統中,鮮有使用兩階段提交和三階段提交協議的場景。相比前兩者而言,有一個更輕量級的實現一致性的協議,就是Paxos協議。

 

Paxos協議

 

Paxos協議的使用有一個前提,就是不存在拜占庭將軍問題。拜占庭位於土耳其的伊斯坦布爾,是東羅馬帝國的首都。當年拜占庭帝國軍隊外出征戰時,必須軍隊內所有將軍和副官達成共識,纔可以決定是否出站。但是因爲軍隊中可能有叛徒和敵軍的簡單存在,因此他們決定出的結果可能並不能代表大多數人的意見。因此如何排除叛徒的影響而達成一致的協議,這就是拜占庭將軍問題。

 

Paxos協議中包含三種角色,分別爲Proposers、Acceptors和Learners。其中Proposers是提出議案者;Acceptors是收到議案後進行判斷的角色;Learners是對通過的議案進行觀察的角色。

 

議案(Proposal)是由一個編號和決議(Value)對組成。決議只有在被Proposers提出後才能被批准,一個Acceptor只能批准(Chosen)一個決議(Value),Learners只能獲得被批准(Chosen)的決議(Value)。

 

Paxos協議完整的投票流程描述如下:

 

1>議員p生成一個比LastTried[p]【p發起的最大表決編號】還大的表決編號b,設置LastTried[p]的值爲b,然後將NextBallot(b)【下一個表決編號】消息發送給一些議員;

 

2>議員q收到這個b大於NextBallot[q]【q的下一個表決編號】的NextBallot[b]消息後,議員q將NextBallot[q]設置爲b,然後發送一個lastVote(b,v)【最新的議案】給p,其中v等於PreviousVote[q]【q投票過的所有表決中編號最大的投票】;

 

3>在某個多數集合Q中的每個成員都收到一個lastVote(b,v)消息後,議員p發起一個編號爲b、法定人數集爲Q、議案爲d的新表決。給Q中的每個議員發送一個beginBallot(b,d)消息;

 

4>在收到一個b=NextBallot[q]的BeginBallot(b,d)消息後,議員q在編號爲b的表決中投票,設置PreviousVote[q]【q投票過的所有表決中編號最大的投票】爲這一票,然後向p發送Voted(b,q);

 

5>p收到Q中每個q的Voted(b,q)消息後,將d記錄到本地,然後發哦少年宮一條Success(d)消息給每個q;

 

6>每個q收到Success(d)消息後,將決議d記錄到本地;

 

流程圖如下:

 

 

整個流程是實現還是比較複雜的,這裏需要好好理解。瞭解整個流程後會發現一個問題:如果系統中同時有人提議案的話,可能會出現碰撞失敗,然後雙方都需要增加議案的編號再提交,而在此提交仍可能存在編號衝突,因此雙方再增加編號提交...這就產生了活鎖。

 

解決辦法可以是在集羣中設置一個leader,每次議案都由這個leader來提交。同時也要保證leader失效後可能進行leader選舉。ZooKeeper的ZAB協議就是以此設計的,這個有機會我們後面再詳說。

 

TCC協議

 

阿里提出了TCC協議,TCC協議將一個任務拆分成Try, Confirm, Cancel三個步驟,正常的流程會先執行Try,如果執行沒有問題,則再執行Confirm,如果執行過程中出了問題,則執行操作的逆操作Cancel。從正常的流程上講,這仍然是一個兩階段提交協議,但是在執行出現問題時有一定的自我修復能力,如果任何參與者一出現了問題,則協調者通過執行操作的逆操作來Cancel之前的操作,達到最終的一致狀態。

可以看出,從時序上來說,如果遇到極端情況,則TCC會有很多問題,例如,如果在取消時一些參與者收到指令,而另一些參與者沒有收到指令,則整個系統仍然是不一致的。對於這種複雜的情況,系統首先會通過補償的方式嘗試自動修復,如果系統無法修復,則必須人工參與解決。

從TCC的邏輯上看,可以說TCC是簡化版的三階段提交協議,解決了兩階段提交協議的阻塞問題,但是沒有解決極端情況下會出現不一致腦裂的問題。然而,TCC通過自動化補償手段,將需要人工處理的不一致情況降到最少,也是種非常有用的解決方案。阿里內部的一些中間件上實現了TCC模式。
 

 

本篇文章中介紹了分佈式一致性的一些協議,那麼針對生產環境的具體場景我們應該如何使用呢?下文見分曉。

 

 

分佈式系統漫談【壹】_發展歷程

分佈式系統漫談【貳】_分佈式系統帶來的問題

分佈式系統漫談【叄】_負載層技術:Nginx

分佈式系統漫談【肆】_負載層技術:CDN

分佈式系統漫談【伍】_遠程調用

分佈式系統漫談【陸】_SOA和微服務

分佈式系統漫談【柒】_微服務的挑戰和解決方案

分佈式系統漫談【捌】_分佈式事務一致性:理論基礎

分佈式系統漫談【玖】_分佈式事務一致性:協議支持

分佈式系統漫談【拾】_分佈式事務一致性:阿里方案

分佈式系統漫談【拾壹】_分佈式事務一致性:秒殺實現

分佈式系統漫談【拾貳】_分庫分錶帶來的問題和解決方案

分佈式系統漫談【拾叄】_緩存帶來的問題和解決方案

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章