關於分散式事務、兩階段提交協議、三階提交協議

HollisChuang發表於2015-12-08

隨著大型網站的各種高併發訪問、海量資料處理等場景越來越多,如何實現網站的高可用、易伸縮、可擴充套件、安全等目標就顯得越來越重要。

為了解決這樣一系列問題,大型網站的架構也在不斷髮展。提高大型網站的高可用架構,不得不提的就是分散式。在《分散式系統的一致性探討》一文中主要介紹了分散式系統中存在的一致性問題。本文將簡單介紹如何有效的解決分散式的一致性問題,其中包括什麼是分散式事務二階段提交三階段提交

分散式一致性回顧

在分散式系統中,為了保證資料的高可用,通常,我們會將資料保留多個副本(replica),這些副本會放置在不同的物理的機器上。為了對使用者提供正確的增\刪\改\差等語義,我們需要保證這些放置在不同物理機器上的副本是一致的。

為了解決這種分散式一致性問題,前人在效能和資料一致性的反反覆覆權衡過程中總結了許多典型的協議和演算法。其中比較著名的有二階提交協議(Two Phase Commitment Protocol)、三階提交協議(Two Phase Commitment Protocol)和Paxos演算法

分散式事務

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

在分散式系統中,各個節點之間在物理上相互獨立,通過網路進行溝通和協調。由於存在事務機制,可以保證每個獨立節點上的資料操作可以滿足ACID。但是,相互獨立的節點之間無法準確的知道其他節點中的事務執行情況。所以從理論上講,兩臺機器理論上無法達到一致的狀態。如果想讓分散式部署的多臺機器中的資料保持一致性,那麼就要保證在所有節點的資料寫操作,要不全部都執行,要麼全部的都不執行。但是,一臺機器在執行本地事務的時候無法知道其他機器中的本地事務的執行結果。所以他也就不知道本次事務到底應該commit還是 roolback。所以,常規的解決辦法就是引入一個“協調者”的元件來統一排程所有分散式節點的執行。

XA規範

X/Open 組織(即現在的 Open Group )定義了分散式事務處理模型。 X/Open DTP 模型( 1994 )包括應用程式( AP )、事務管理器( TM )、資源管理器( RM )、通訊資源管理器( CRM )四部分。一般,常見的事務管理器( TM )是交易中介軟體,常見的資源管理器( RM )是資料庫,常見的通訊資源管理器( CRM )是訊息中介軟體。 通常把一個資料庫內部的事務處理,如對多個表的操作,作為本地事務看待。資料庫的事務處理物件是本地事務,而分散式事務處理的物件是全域性事務。 所謂全域性事務,是指分散式事務處理環境中,多個資料庫可能需要共同完成一個工作,這個工作即是一個全域性事務,例如,一個事務中可能更新幾個不同的資料庫。對資料庫的操作發生在系統的各處但必須全部被提交或回滾。此時一個資料庫對自己內部所做操作的提交不僅依賴本身操作是否成功,還要依賴與全域性事務相關的其它資料庫的操作是否成功,如果任一資料庫的任一操作失敗,則參與此事務的所有資料庫所做的所有操作都必須回滾。 一般情況下,某一資料庫無法知道其它資料庫在做什麼,因此,在一個 DTP 環境中,交易中介軟體是必需的,由它通知和協調相關資料庫的提交或回滾。而一個資料庫只將其自己所做的操作(可恢復)影射到全域性事務中。

XA 就是 X/Open DTP 定義的交易中介軟體與資料庫之間的介面規範(即介面函式),交易中介軟體用它來通知資料庫事務的開始、結束以及提交、回滾等。 XA 介面函式由資料庫廠商提供。

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

2PC

二階段提交(Two-phaseCommit)是指,在計算機網路以及資料庫領域內,為了使基於分散式系統架構下的所有節點在進行事務提交時保持一致性而設計的一種演算法(Algorithm)。通常,二階段提交也被稱為是一種協議(Protocol))。在分散式系統中,每個節點雖然可以知曉自己的操作時成功或者失敗,卻無法知道其他節點的操作的成功或失敗。當一個事務跨越多個節點時,為了保持事務的ACID特性,需要引入一個作為協調者的元件來統一掌控所有節點(稱作參與者)的操作結果並最終指示這些節點是否要把操作結果進行真正的提交(比如將更新後的資料寫入磁碟等等)。因此,二階段提交的演算法思路可以概括為:參與者將操作成敗通知協調者,再由協調者根據所有參與者的反饋情報決定各參與者是否要提交操作還是中止操作。

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

準備階段

事務協調者(事務管理器)給每個參與者(資源管理器)傳送Prepare訊息,每個參與者要麼直接返回失敗(如許可權驗證失敗),要麼在本地執行事務,寫本地的redo和undo日誌,但不提交,到達一種“萬事俱備,只欠東風”的狀態。

可以進一步將準備階段分為以下三個步驟:

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

2)參與者節點執行詢問發起為止的所有事務操作,並將Undo資訊和Redo資訊寫入日誌。(注意:若成功這裡其實每個參與者已經執行了事務操作)

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

提交階段

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

接下來分兩種情況分別討論提交階段的過程。

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

success

1)協調者節點向所有參與者節點發出”正式提交(commit)”的請求。

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

3)參與者節點向協調者節點傳送”完成”訊息。

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

如果任一參與者節點在第一階段返回的響應訊息為”中止”,或者 協調者節點在第一階段的詢問超時之前無法獲取所有參與者節點的響應訊息時:

fail

1)協調者節點向所有參與者節點發出”回滾操作(rollback)”的請求。

2)參與者節點利用之前寫入的Undo資訊執行回滾,並釋放在整個事務期間內佔用的資源。

3)參與者節點向協調者節點傳送”回滾完成”訊息。

4)協調者節點受到所有參與者節點反饋的”回滾完成”訊息後,取消事務。

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

二階段提交看起來確實能夠提供原子性的操作,但是不幸的事,二階段提交還是有幾個缺點的:

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

2、單點故障。由於協調者的重要性,一旦協調者發生故障。參與者會一直阻塞下去。尤其在第二階段,協調者發生故障,那麼所有的參與者還都處於鎖定事務資源的狀態中,而無法繼續完成事務操作。(如果是協調者掛掉,可以重新選舉一個協調者,但是無法解決因為協調者當機導致的參與者處於阻塞狀態的問題)

3、資料不一致。在二階段提交的階段二中,當協調者向參與者傳送commit請求之後,發生了區域性網路異常或者在傳送commit請求過程中協調者發生了故障,這回導致只有一部分參與者接受到了commit請求。而在這部分參與者接到commit請求之後就會執行commit操作。但是其他部分未接到commit請求的機器則無法執行事務提交。於是整個分散式系統便出現了資料部一致性的現象。

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

由於二階段提交存在著諸如同步阻塞、單點問題、腦裂等缺陷,所以,研究者們在二階段提交的基礎上做了改進,提出了三階段提交。

3PC

三階段提交(Three-phase commit),也叫三階段提交協議(Three-phase commit protocol),是二階段提交(2PC)的改進版本。

3

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

1、引入超時機制。同時在協調者和參與者中都引入超時機制。
2、在第一階段和第二階段中插入一個準備階段。保證了在最後提交階段之前各參與節點的狀態是一致的。

也就是說,除了引入超時機制之外,3PC把2PC的準備階段再次一分為二,這樣三階段提交就有CanCommitPreCommitDoCommit三個階段。

CanCommit階段

3PC的CanCommit階段其實和2PC的準備階段很像。協調者向參與者傳送commit請求,參與者如果可以提交就返回Yes響應,否則返回No響應。

1.事務詢問 協調者向參與者傳送CanCommit請求。詢問是否可以執行事務提交操作。然後開始等待參與者的響應。

2.響應反饋 參與者接到CanCommit請求之後,正常情況下,如果其自身認為可以順利執行事務,則返回Yes響應,並進入預備狀態。否則反饋No

PreCommit階段

協調者根據參與者的反應情況來決定是否可以記性事務的PreCommit操作。根據響應情況,有以下兩種可能。

假如協調者從所有的參與者獲得的反饋都是Yes響應,那麼就會執行事務的預執行。

1.傳送預提交請求 協調者向參與者傳送PreCommit請求,並進入Prepared階段。

2.事務預提交 參與者接收到PreCommit請求後,會執行事務操作,並將undo和redo資訊記錄到事務日誌中。

3.響應反饋 如果參與者成功的執行了事務操作,則返回ACK響應,同時開始等待最終指令。

假如有任何一個參與者向協調者傳送了No響應,或者等待超時之後,協調者都沒有接到參與者的響應,那麼就執行事務的中斷。

1.傳送中斷請求 協調者向所有參與者傳送abort請求。

2.中斷事務 參與者收到來自協調者的abort請求之後(或超時之後,仍未收到協調者的請求),執行事務的中斷。

doCommit階段

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

執行提交

1.傳送提交請求 協調接收到參與者傳送的ACK響應,那麼他將從預提交狀態進入到提交狀態。並向所有參與者傳送doCommit請求。

2.事務提交 參與者接收到doCommit請求之後,執行正式的事務提交。並在完成事務提交之後釋放所有事務資源。

3.響應反饋 事務提交完之後,向協調者傳送Ack響應。

4.完成事務 協調者接收到所有參與者的ack響應之後,完成事務。

中斷事務 協調者沒有接收到參與者傳送的ACK響應(可能是接受者傳送的不是ACK響應,也可能響應超時),那麼就會執行中斷事務。

1.傳送中斷請求 協調者向所有參與者傳送abort請求

2.事務回滾 參與者接收到abort請求之後,利用其在階段二記錄的undo資訊來執行事務的回滾操作,並在完成回滾之後釋放所有的事務資源。

3.反饋結果 參與者完成事務回滾之後,向協調者傳送ACK訊息

4.中斷事務 協調者接收到參與者反饋的ACK訊息之後,執行事務的中斷。

在doCommit階段,如果參與者無法及時接收到來自協調者的doCommit或者rebort請求時,會在等待超時之後,會繼續進行事務的提交。(其實這個應該是基於概率來決定的,當進入第三階段時,說明參與者在第二階段已經收到了PreCommit請求,那麼協調者產生PreCommit請求的前提條件是他在第二階段開始之前,收到所有參與者的CanCommit響應都是Yes。(一旦參與者收到了PreCommit,意味他知道大家其實都同意修改了)所以,一句話概括就是,當進入第三階段時,由於網路超時等原因,雖然參與者沒有收到commit或者abort響應,但是他有理由相信:成功提交的機率很大。 )

2PC與3PC的區別

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


瞭解了2PC和3PC之後,我們可以發現,無論是二階段提交還是三階段提交都無法徹底解決分散式的一致性問題。Google Chubby的作者Mike Burrows說過, there is only one consensus protocol, and that’s Paxos” – all other approaches are just broken versions of Paxos. 意即世上只有一種一致性演算法,那就是Paxos,所有其他一致性演算法都是Paxos演算法的不完整版。後面的文章會介紹這個公認為難於理解但是行之有效的Paxos演算法。

參考資料:

  1. 分散式協議之兩階段提交協議(2PC)和改進三階段提交協議(3PC)
  2. 關於分散式事務、兩階段提交、一階段提交、Best Efforts 1PC模式和事務補償機制的研究
  3. 兩階段提交協議與三階段提交協議

相關文章