分佈式事務 - 兩階段提交與三階段提交

摘要: 在分佈式系統中,著有CAP理論,該理論由加州大學伯克利分校的Eric Brewer教授提出,該理論闡述了在一個分佈式系統中不可能同時滿足一致性(Consistency)、可用性(Availability),以及分區容錯性(Partition tolerance)。

  在分佈式系統中,著有CAP理論,該理論由加州大學伯克利分校的Eric Brewer教授提出,該理論闡述了在一個分佈式系統中不可能同時滿足一致性(Consistency)、可用性(Availability),以及分區容錯性(Partition tolerance)。

  • 一致性
    在分佈式系統中數據往往存在多個副本,一致性描述的是這些副本中的數據在內容和組織上的一致。
  • 可用性
    可用性描述了系統對用戶的服務能力,所謂可用是指在用戶容忍的時間範圍內返回用戶期望的結果。
  • 分區容錯性
    分佈式系統通常由多個節點構成,這些節點通常分佈在不同的網絡中,然而網絡始終是不可靠的,所以存在分佈式集羣中的節點因爲網絡通信故障導致被孤立成一個個小集羣的可能性,分區容錯性要求在出現這種情況下系統仍然能夠對外提供一致性的可用服務。

  對於一個分佈式系統,我們始終要假設網絡是不可靠的,所以分區容錯性是對一個分佈式系統最基本的要求,所以我們更多的是嘗試在可用性和一致性之間尋找一個平衡點。讓分佈式集羣始終對外提供可用的一致性服務一直是富有挑戰和趣味的一項任務。暫且拋開可用性,拿一致性來說,對於關係型數據庫我們通常利用事務來保證數據的一致性,當我們的數據量越來越大,大到單庫已經無法承擔時,我們不得不採取分庫分表的策略對數據庫實現拆分,構建分佈式數據庫集羣,這樣可以將一個數據庫的壓力分攤到多個數據庫,極大的提升了數據庫的存儲和響應能力,但是也爲我們使用數據庫帶來了許多的限制,比如主鍵的全局唯一、聯表查詢、數據聚合等等,另外一個相當棘手的問題就是數據庫的事務由原先的單庫事務變成了現在的分佈式事務。
  分佈式事務的實現並不是很難,比如下文要展開的兩階段提交(2PC:Two-Phrase Commit)和三階段提交(3PC:Three-Phrase Commit)都給我們提供了思路,但是如果要保證數據的強一致性,並要求對外提供可用的服務,那麼就變成了一個幾乎不可能的任務(至少目前是),因此很多分佈式系統對於數據強一致性都敬而遠之,本人在之前項目的架構設計中也花費不少時間在系統的一致性和可用性之間尋找平衡。

兩階段提交協議(2PC:Two-Phrase Commit)

  兩階段提交協議的目標在於在分佈式系統中保證數據的一致性,許多分佈式系統採用該協議提供對分佈式事務的支持(提供但不一定有人用,呵呵~)。顧名思義,該協議將一個分佈式的事務過程拆分成兩個階段:投票階段和事務提交階段。爲了讓整個數據庫集羣能夠正常的運行,該協議指定了一個“協調者”單點,用於協調整個數據庫集羣的運行,爲了簡化描述,我們將數據庫裏面的各個節點稱爲“參與者”,三階段提交協議中同樣包含“協調者”和“參與者”這兩個定義。

第一階段:投票階段

該階段的主要目的在於打探數據庫集羣中的各個參與者是否能夠正常的執行事務,具體步驟如下:

1. 協調者向所有的參與者發送事務執行請求,並等待參與者反饋事務執行結果。
2. 事務參與者收到請求之後,執行事務,但不提交,並記錄事務日誌。
3. 參與者將自己事務執行情況反饋給協調者,同時阻塞等待協調者的後續指令。
第二階段:事務提交階段

在第一階段協調者的詢盤之後,各個參與者會回覆自己事務的執行情況,這時候存在三種可能:

1. 所有的參與者回覆能夠正常執行事務
2. 一個或多個參與者回覆事務執行失敗
3. 協調者等待超時。

對於第一種情況,協調者將向所有的參與者發出提交事務的通知,具體步驟如下:

1. 協調者向各個參與者發送commit通知,請求提交事務。
2. 參與者收到事務提交通知之後,執行commit操作,然後釋放佔有的資源。
3. 參與者向協調者返回事務commit結果信息。

事務提交時序圖

對於第二、三種情況,協調者均認爲參與者無法正常成功執行事務,爲了整個集羣數據的一致性,所以要向各個參與者發送事務回滾通知,具體步驟如下:

1. 協調者向各個參與者發送事務rollback通知,請求回滾事務。
2. 參與者收到事務回滾通知之後,執行rollback操作,然後釋放佔有的資源。
3. 參與者向協調者返回事務rollback結果信息。

事務回滾時序圖

  兩階段提交協議解決的是分佈式數據庫數據強一致性問題,其原理簡單,易於實現,但是缺點也是顯而易見的,主要缺點如下:

  • 單點問題
    協調者在整個兩階段提交過程中扮演着舉足輕重的作用,一旦協調者所在服務器宕機,那麼就會影響整個數據庫集羣的正常運行,比如在第二階段中,如果協調者因爲故障不能正常發送事務提交或回滾通知,那麼參與者們將一直處於阻塞狀態,整個數據庫集羣將無法提供服務。

  • 同步阻塞
    兩階段提交執行過程中,所有的參與者都需要聽從協調者的統一調度,期間處於阻塞狀態而不能從事其他操作,這樣效率及其低下。

  • 數據不一致性
    兩階段提交協議雖然爲分佈式數據強一致性所設計,但仍然存在數據不一致性的可能,比如在第二階段中,假設協調者發出了事務commit的通知,但是因爲網絡問題該通知僅被一部分參與者所收到並執行了commit操作,其餘的參與者則因爲沒有收到通知一直處於阻塞狀態,這時候就產生了數據的不一致性。

三階段提交協議(2PC:Three-Phrase Commit)

  針對兩階段提交存在的問題,三階段提交協議通過引入一個“預詢盤”階段,以及超時策略來減少整個集羣的阻塞時間,提升系統性能。三階段提交的三個階段分別爲:can_commit,pre_commit,do_commit。

第一階段:can_commit

  該階段協調者會去詢問各個參與者是否能夠正常執行事務,參與者根據自身情況回覆一個預估值,相對於真正的執行事務,這個過程是輕量的,具體步驟如下:

1. 協調者向各個參與者發送事務詢問通知,詢問是否可以執行事務操作,並等待回覆
2. 各個參與者依據自身狀況回覆一個預估值,如果預估自己能夠正常執行事務就返回確定信息,並進入預備狀態,否則返回否定信息
第二階段:pre_commit

本階段協調者會根據第一階段的詢盤結果採取相應操作,詢盤結果主要有三種:

1. 所有的參與者都返回確定信息
2. 一個或多個參與者返回否定信息
3. 協調者等待超時

針對第一種情況,協調者會向所有參與者發送事務執行請求,具體步驟如下:

1. 協調者向所有的事務參與者發送事務執行通知
2. 參與者收到通知後,執行事務,但不提交
3. 參與者將事務執行情況返回給客戶端

在上面的步驟中,如果參與者等待超時,則會中斷事務。 針對第二、三種情況,協調者認爲事務無法正常執行,於是向各個參與者發出abort通知,請求退出預備狀態,具體步驟如下:

1. 協調者向所有事務參與者發送abort通知
2. 參與者收到通知後,中斷事務

事務中斷時序圖

第三階段:do_commit

  如果第二階段事務未中斷,那麼本階段協調者將會依據事務執行返回的結果來決定提交或回滾事務,分爲三種情況:

1. 所有的參與者都能正常執行事務
2. 一個或多個參與者執行事務失敗
3. 協調者等待超時

針對第一種情況,協調者向各個參與者發起事務提交請求,具體步驟如下:

1. 協調者向所有參與者發送事務commit通知
2. 所有參與者在收到通知之後執行commit操作,並釋放佔有的資源
3. 參與者向協調者反饋事務提交結果

事務提交時序圖
針對第二、三種情況,協調者認爲事務無法正常執行,於是向各個參與者發送事務回滾請求,具體步驟如下:

1. 協調者向所有參與者發送事務rollback通知
2. 所有參與者在收到通知之後執行rollback操作,並釋放佔有的資源
3. 參與者向協調者反饋事務提交結果

事務回滾時序圖
  在本階段如果因爲協調者或網絡問題,導致參與者遲遲不能收到來自協調者的commit或rollback請求,那麼參與者將不會如兩階段提交中那樣陷入阻塞,而是等待超時後繼續commit。相對於兩階段提交雖然降低了同步阻塞,但仍然無法避免數據的不一致性。

  在分佈式數據庫中,如果期望達到數據的強一致性,那麼服務基本沒有可用性可言,這也是爲什麼許多分佈式數據庫提供了跨庫事務,但也只是個擺設的原因,在實際應用中我們更多追求的是數據的弱一致性或最終一致性,爲了強一致性而丟棄可用性是不可取的。

轉載自:https://my.oschina.net/wangzhenchao/blog/736909

發佈了59 篇原創文章 · 獲贊 26 · 訪問量 22萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章