一致性算法,兩階段提交協議

分佈式事務是指會涉及到操作多個數據庫的事務,其實就是將對同一庫事務的概念擴大到了對多個庫的事務。目的是為了保證分佈式系統中的數據一致性。分佈式事務處理的關鍵是必須有一種方法可以知道事務在任何地方所做的所有動作,提交或回滾事物的決定必須產生統一的結果(全部提交或全部回滾)

XA規範

X/Open組織定義了分佈式事務處理模型。X/Open DTP模型包括應用程序(AP),事務管理(TM),資源管理器(RM),通信資源管理器(CRM)四部分。一般廠家的事務管理器(TM)是交易中間件,廠家的資源管理器(RM)是數據庫,常見的通信資源管理器(CRM)是消息中間件。通常把一個數據庫內部的事務處理,如對多個表的操作,作為本地事務看待。數據庫的事務處理對象時本地事務而分佈式事務處理的對象時全局事務,所謂全局事務,是指分佈式事務處理環境中,多個數據庫可能要共同完成同一個工作,這個工作即是全局事務,在一個DTP環境中,交易中間件是必需的,由它通知和協調相關數據庫的提交或回滾,而一個數據庫只將其自己所做的操作映射到全局事務中。

XA就是X/Open DTP定義的交易中間件與數據庫之間的接口規範,交易中間件用它來通知數據庫事務的開始,結束以及提交,回滾等。XA接口函數由數據庫廠商提供。

二階段提交協議和三階段提交協議就是根據這一思想衍生出來的。可以說二階段提交是實現XA分佈式協議的關鍵(兩階段提交主要保證了分佈式事務的原子性:即所有節點要麼全做要麼全不做)

2PC

二階段提交(Two-phaseCommit)是指,為了使基於分佈式架構下的所有節點在進行事務提交時保持一致性而設計的一種算法。二階段提交也被稱為一種協議,二階段的算法思路可以概括為:參與者將操作成敗通知協調者,再由協調者根據所有參與者的反饋情報決定各參與者是否要提交操作還是中止操作。

所謂兩階段是指:第一階段:準備階段(投票階段)和第二階段:提交階段(執行階段)

準備階段

事務協調者(事務管理器)給每個參與者(資源管理器)發送prepare消息,每個參與者要麼直接返回失敗,要麼在本地執行事務,寫本地的redo和undo日誌,但未提交,到達一種"萬事俱備只欠東風"的狀態。

1.協調者節點向所有參與者節點詢問是否可以執行提交操作(vote),並等待各參與者節點的響應

2.參與者節點執行詢問發起為止的所有事務操作,並將undo和redo信息寫入日誌(若成功其實每個參與者已經執行了事務操作)

3.各參與者節點響應協調者節點發起的詢問,如果參與者節點的事務操作實際執行成功,則它返回一個"同意"消息;如果參與者節點的事務操作實際執行失敗,則它返回一個"中止"消息。

提交階段

如果協調者收到了參與者的失敗消息或者超時,直接給每個參與者發送回滾(Rollback)消息;否則,發送提交(Commit)消息;參與者根據協調者的指令執行提交或回滾操作,釋放所有事務處理過程中使用的鎖資源(必須在最後階段釋放鎖資源)

當協調者節點從所有參與者節點獲得相應消息都為“同意”時:

一致性算法,兩階段提交協議/三階段提交協議/柔性事務(TCC)

1.協調者節點向所有參與者節點發出正式提交的請求

2.參與者節點正式完成操作,並釋放在整個事務期間內佔用的資源。

3.參與者節點向協調者節點發送“完成”消息

4.協調者節點受到所有參與者節點反饋的“完成”消息後,完成事務。

不管最後結果如何,第二階段都會結束當前事務。

回滾操作亦是如此

而極端提交有幾個缺點:

1.同步阻塞問題

在執行過程中,所有參與節點都是事務阻塞型的,當參與者佔用公共資源時,其他第三方節點訪問公共資源不得不處於阻塞狀態。

2.單點故障

由於協調者的重要性,一旦協調者發送故障,參與者會一直阻塞下去。尤其在第二階段,協調者發送故障,那麼所有參與者還都處於鎖定事務資源的專題,而無法繼續完成事務操作。

3.數據不一致

當協調者發向參與者發送commit請求之後,發生了局部網絡異常或者在發送commit請求後協調者發生了故障,這會導致只有一部分參與者接受到了commit請求。而在這部分參與者接到commit之後會執行commit操作,但其他部分為收到commit的機器則服務執行事務提交。於是整個分佈式系統便出現數據不一致的現象

4.二階段無法解決的問題:協調者再發出commit消息後宕機,而唯一接收到這條消息的參與者同時也宕機了。那麼即使協調者通過選舉產生了新的協調者,這條事務的狀態也是不確定的,沒人知道事務是否已經提交。

3PC

三階段提交(Three-phase commit)是二階段提交的改進版本

一致性算法,兩階段提交協議/三階段提交協議/柔性事務(TCC)

與二階段提交的不同是,三階段提交有兩個改動點。

1.引入超時機制同時在協調者和參與者中都引入超時機制

2.在第一個階段和第二階段中插入一個準備階段,保證了在最後提交階段之前各參與節點的狀態一致的。

除了引入超時機制之外,3PC把2PC的準備階段再次一份為二,這樣三階段提交就有CanCommit,PreCommit,DoCommit三個階段。

CanCommit階段

3pc的cancommit階段與2pc的準備階段很像,協調者向參與者發送commit請求,參與者如果可以提交就返回yes響應,否則返回no響應

PreCommit階段

協調者根據參與者的反映情況來決定是否可以繼續事務的PreCommit操作。

1.假如協調者從所有參與者獲得反饋都是yes響應,那麼就會執行事務的預執行(發送預提交請求:協調者向參與者發送PreCommit請求,並進入Prepared階段;事務預提交:參與者接收到PreCommit請求後,會執行事務操作,並將undo和redo信息記錄到事務日誌中;響應反饋:如果參與者成功的執行了事務操作,則返回ACK響應,同時開始等待最終指令)

2.假如任何一個參與者向協調者發送了no響應,或者等待超時後,協調者沒有收到參與者的響應,那麼久執行事務的中斷。(發送中斷:協調者向所有參與者發送abort請求;中斷事務:參與者接收到來自協調者的abort請求後(或超時之後,仍未收到協調者的請求)執行事務的中斷)

DoCommit階段

該階段進行真正的事務提交。可以分為兩種情況

執行提交:1.發送提交請求,協調者收到參與者發送ACK響應,那麼他將從預提交狀態進入提交狀態,並向所有參與者發送doCommit請求;2.事務提交,參與者收到doCommit請求後,執行正式的事務提交,並在完成事務提交之後釋放所有事務資源;3.響應反饋,事務提交完成後,向協調者發送Ack響應;4.完成事務,協調者接收到所有參與者的ack響應後,完成事務。

中斷事務:協調者沒有接收到參與者發送的ACK響應(可能是接受者發送的不是ACK響應,也可能是響應超時),那麼就會執行中斷事務;1.發送中斷請求,協調者向所有參與者發送abort請求;2.事務回滾,參與者接收到abort請求後,利用其在階段二記錄的undo信息來執行事務的回滾操作,並在完成回滾之後釋放所有的事務資源;3.反饋結果,參與者完成是服務回滾後,向協調者發送ACK消息;4.中斷事務,協調者接收到參與者反饋的ACK消息後,執行事務的中斷。

在doCommit階段,如果參與者無法及時收到來自協調者的doCommit或者abort請求,會在等待超時之後,會繼續進行事務的提交。

2pc與3pc的區別

相對於2pc,3pc主要解決的單點故障問題,並減少阻塞,因為一旦參與者無法及時收到來自協調者的信息之後,它會默認執行commit,而不會一直持有事務資源並處於阻塞狀態。但是這種機制也會導致數據一致性問題,因為由於網絡原因,協調者發送的abort響應沒有及時被參與者接收到,那麼參與者在等待超時之後執行了commit操作。這樣就和其他接到abort命令並執行回滾的參與者之間存在數據不一致的情況。

柔性事務

TCC是分佈式系統場景下的一種事務解決方案,他和傳統的分佈式事務的最大區別是分佈式事務會鎖資源,而tcc屬於柔性事務不會鎖資源。

實現柔性事務三種方式,tcc,消息機制,補償型。

一致性算法,兩階段提交協議/三階段提交協議/柔性事務(TCC)

TCC即Try-Confirm-Cancel

try:資源預留&鎖定,事務發起方將調用服務提供方的Try方法來鎖定業務所需要的所有資源。

Confirm:確認執行業務邏輯操作,這裡使用的資源一定都是在try中預留的資源,try+confirm組合起來是一次完整的業務邏輯。

Cancel:取消執行業務邏輯,這裡和普通的補償性事務不同,因為try階段只是預留資源,並未真正執行操作,因此取消操作只需要釋放try階段預留的資源,而不需要執行數據庫操作來補償。

用戶通過編碼實現tcc併發布成服務,這個tcc服務就可以作為資源參與到分佈式事務中;事務管理器分2階段協調所有的TCC資源,使得所有TCC資源狀態最終都是一致,要麼全部提交,要麼全部回滾;TCC自編碼的特性決定TCC資源管理器可以跨DB、跨應用實現資源管理,將對不同的DB訪問、不同的業務操作通過編碼方式轉換一個原子操作,解決了複雜業務場景下的事務問題;同時TCC的每一個操作對於DB來講都是一個本地DB事務,操作結束則本地DB事務結束,數據庫的資源也就被釋放;這就規避了數據庫層面的2PC對資源佔用導致的性能低下問題。

TCC與2PC協議比較

TCC位於業務服務層而非資源層

TCC沒有單獨的準備(Prepare)階段,Try操作兼備資源操作與準備能力 Try操作可以靈活選擇業務資源的鎖定粒度(以業務定粒度)

TCC有較高開發成本

參考:拜託,面試請不要再問我TCC分佈式事務的實現原理!

異步確保

2PC的處理過程中一個很大的問題是,存在大量的同步等待,這便意味著操作之間的強耦合,一旦發生了失敗或是超時,造成的影響往往是災難性的。但是分佈式情況下,超時和失敗又是很可能出現的情況,所以2PC手段沒法保證系統的可用性。

那麼怎麼優化呢?可以將操作解耦,使用消息隊列(或者某種可靠的通信機制)來連接不同的實例上的操作。這樣的通信機制使操作異步化,於是我們還需要一個能夠確保消息執行成功的確保機制,以上兩點的綜合就是現在最常用的柔性事務解決方案,我們暫且叫它“異步確保”(因為這種方案並非有一個統一的叫法),核心思路其實就是:用消息隊列保證最終一致性。

重試與冪等

在接下來講到的各種思路中,我們都無法避免一個問題,那就是接口調用或者說操作的失敗,分佈式情況下系統的狀態往往不如單機條件下確定,所以可能經常需要重試,而不是一失敗就回滾。

冪等性,其實是一個數學概念。冪等函數,或冪等方法,是指可以使用相同參數重複執行,並能獲得相同結果的函數,如:f(f(x)) = f(x)

在編程中一個冪等操作的特點是其任意多次執行所產生的影響均與一次執行的影響相同。也就是說,同一個方法,使用同樣的參數,調用多次產生的業務結果與調用一次產生的業務結果相同。這一個要求其實也比較好理解,因為要保證數據的最終一致性,很多解決方法都會有很多重試的操作,如果一個方法不保證冪等,那麼將無法被重試。冪等操作的實現方式有多種,如在系統中緩存所有的請求與處理結果、檢測到重複操作後,直接返回上一次的處理結果等。

可補償操作

提到事務,為了保證原子性,就可能發生commit和rollback,那麼在分佈式事務中,要想進行rollback,就需要提供可補償操作。比如上面的訂單處理的例子中,在調用積分服務給積分帳戶增加積分操作執行之後,經過分佈式事務協調,最終決定回滾整個事務,那麼就需要提供一個調用積分服務給積分帳戶扣減積分的操作。並且,補償操作同時也需要滿足冪等性。

記錄日誌+補償。記錄事務的開始和結束狀態。事務根據日誌記錄找回事務的當前執行狀態,並根據狀態決定重試異常步驟,也就是正向補償,或者回滾上一次執行步驟,也就是反向補償。


分享到:


相關文章: