分佈式事務,強一致性方案有哪些?|分佈式事務系列(二)

繼續分佈式事務專題:本文講解的是強一致性解決方案 XA、2PC、3PC。

接着上一篇文章,由於我們非常關注數據的一致性,所以總體來說按照一致性強弱的維度分類,解決分佈式事務問題可以有以下方案:

本文我們講解強一致性方案:XA 協議,2PC(兩階段提交)、3PC(三階段提交)。如果上一篇基礎內容還沒有讀,請移步。

XA 協議

因爲在使用本地事務的過程中有數據褲引擎的保證,但是如果是多數據源場景下就有了一致性問題,爲此產生了全局事務。全局事務並不限定數據源是一個還是多個,但是在分佈式系統場景下我們都當作多數據源的分佈式事務來討論。

爲了解決分佈式事務的一致性問題、統一標準,1991 年 X/Open 組織(後來併入國際開放標準組織 The Open Group )提出了一套名爲 X/Open XA(XA 是 eXtended Architecture 的縮寫)的處理事務架構,制定了標準化的模型和接口。它定義了全局的事務管理器(Transaction Manager,用於協調全局事務)和局部的資源管理器(Resource Manager,用於驅動本地事務)之間的通信接口。1994 年國際開放標準組織 The Open Group 在 1994 年定義了分佈式事務處理模型 DTP(Distributed Transaction Processing Reference Model,X/Open XA),XA 協議成爲事務模型事實上的標準。

在 XA 協議下有三種核心角色:

基於 XA 規範 Java 中實現了 JTA ,MySQL、Oracle 也都對其做了支持,XA 約定了 TM 和 RM 之間雙向通訊的接口規範,能在一個 TM 和多個 RM 之間形成通信橋樑,通過協調多個數據源的一致動作,實現全局事務的統一提交或者統一回滾。

在 XA 協議下如果我們想實現一個商城的下單、扣款、扣庫存功能,可以實現如下僞代碼。

orderTransaction.begin();//訂單
balanceTransaction.begin();//餘額
warehouseTransaction.begin();//倉庫
try{
  order.submit();//下單
  balance.pay();//付款
  warehouse.decrease();//扣減庫存
  orderTransaction.commit();//訂單
 balanceTransaction.commit();//餘額
  //故障點
 warehouseTransaction.commit();//倉庫
}catch(Exception e){
  //無法回滾
  orderTransaction.rollback();//訂單
 balanceTransaction.rollback();//餘額
 warehouseTransaction.rollback();//倉庫
}

訂單、餘額、倉庫三個數據源分別開啓事務,如果成功分別提交事務。但是這段邏輯有個很大的問題就是如果訂單、餘額事務提交成功,但是倉庫 commit 發生異常,爲了保證一致性需要全部迴歸,但是由於訂單、餘額已提交所以無法回滾,發生了數據不一致場景。面對這種問題,XA 協議選擇了兩階段提交(2PC)作爲實現,從而在多個數據庫資源下保證 ACID 四個特性。

關於兩階段的內容我們在這裏不做過多解釋,具體請看後面兩階段專題。

優缺點

由於 XA 使用兩階段提交,因此兩階段提交的優點和缺點通常適用於 XA。

優點:XA 允許跨多種異構技術的原子事務(不同類型數據源、消息中間件),解決了分佈式事務的問題,而傳統數據庫事務僅限於單一數據庫。

缺點:準備階段會長時間持有資源,造成系統性能降低,並且協調者會有單點故障的問題。

總結

如果你看到這感覺暈暈乎乎,還是沒明白 XA 到底是個啥,那需要看一下這段總結。

XA 協議是一種用於處理分佈式事務的協議,定義了 TM、RM、應用程序集中模型和接口,大多數實現 XA 的都是一些關係型數據庫(包括 MySQL,SQL Server、PostgreSQL 和 Oracle)和消息中間件(包括 ActiveMQ,HornetQ,MSMQ 和 IBM MQ),Java 中的 JTA 也實現了 XA 規範接口。

兩階段提交(Two-Phase Commit,簡稱 2PC)是 XA 協議中的一種實現方式。通過接口規範,應用程序訪問並使用 RM 的資源,並通過 TM 的事務接口(TX interface)定義需要執行的事務操作,然後 TM 和 RM 會基於 XA 規範,執行二階段提交協議進行事務的提交 / 回滾。

兩階段提交 2PC

階段提交(Two-Phase Commit,簡稱 2PC)是分佈式事務處理中的一種算法,是最爲經典的分佈式事務解決方案之一。用於確保在涉及多個節點(或進程)的事務中,所有節點要麼全部提交(commit),要麼全部回滾(rollback),從而保持數據的一致性。

爲了協調分佈式環境下的不同服務,它通過一箇中心協調器來協調多個參與者的事務。該協調器在第一階段(準備階段)詢問所有參與者是否可以提交事務,如果所有參與者都準備好了,則在第二階段(提交階段)通知所有參與者提交事務。

兩階段提交算法是由 Jim Gray 和 Andreas Reuter 在 1981 年提出的,並在 1983 年發表了相關論文《事務處理:概念和技術》(Transaction Processing: Concepts and Techniques)中進行了詳細討論。這篇論文是分佈式事務處理領域的經典著作之一,對於今天的分佈式系統設計仍然具有重要的參考價值。

兩階段過程

兩階段指的是分佈式事務的提交過程分爲兩個階段,具體流程如下:

第一階段(投票階段):

  1. 協調者(Coordinator)向參與者(Participant)發出請求執行事務的消息。

  2. 參與者執行事務,並將 Undo 和 Redo 信息記錄在事務日誌中,但並不提交事務,也就是說此時資源已經被鎖定。

  3. 參與者向協調者發送 “投票” 消息,表示事務是否執行成功。如果參與者執行成功,則返回 “同意(Agree)” 消息;如果參與者執行失敗,則返回 “否決(Abort)” 消息。

第二階段(提交階段):

  1. 協調者收到所有參與者的投票信息,如果所有參與者都返回 “同意” 消息,則協調者向所有參與者發送 “提交(Commit)” 消息。

  2. 參與者收到 “提交” 消息後,執行提交操作,並釋放在第一階段中申請的所有資源。

  3. 如果任何一個參與者返回 “否決” 消息,協調者將向所有參與者發送 “回滾(Rollback)” 消息。

  4. 參與者收到 “回滾” 消息後,執行回滾操作,並釋放在第一階段中申請的所有資源。

需要注意的是,兩階段提交協議有可能會存在 “阻塞” 的問題,也就是說,在第一階段中,如果有任何一個參與者無法響應,那麼協調者將一直等待,直到超時。在這種情況下,需要採取超時機制和其他優化手段來提高系統的可用性和性能。

因爲它需要在所有兩階段的缺點是存在單點故障和阻塞問題。

前提條件

兩階段提交的成立是有前提條件的。

兩階段的問題

雖然兩階段可以解決大部分場景下的事務的一致性問題,並且原理簡單,但是它存在以下一些缺點。

  1. 資源佔用導致同步阻塞問題:在第一階段投票之後,參與的事務的每個節點資源都被鎖定,並且在第二階段參與者等到協調者的響應才能繼續執行,事務時間越長資源佔用時間越長。並且如果協調者發生故障,參與者會一直等待其響應,這回導致整個系統的性能受到影響。

  2. 單點故障問題:在 2PC 中,協調者扮演着關鍵的角色,如果協調者發生故障,整個事務就會失敗。協調者等待參與者回覆時可以有超時機制,允許參與者宕機,但參與者等待協調者指令時無法做超時處理,事務將長時間持續。因此,協調者成爲了系統的單點故障,這會影響整個系統的可用性。

  3. 數據不一致問題:其實兩階段提交有個前提,就是網絡狀況短時間內穩定,這樣可以保證第一階段投票完畢後第二個階段可以順利提交。並且必須假設因故障而下線的節點最終能宕機恢復,在第一階段中可以有 Undo Log 可以保證。但是如果在第一階段之後,協調者向參與者發送的提交請求丟失或者超時或者節點宕機,那麼就會導致一些參與者已經提交了數據,而另外一些參與者卻沒有提交數據,從而導致數據不一致。

  1. 可擴展性問題:在大規模分佈式系統中,參與者數量可能會非常龐大,這會導致協調者需要維護大量的狀態信息,從而影響系統的可擴展性。

三階段提交 3PC

三階段提交(Three-Phase Commit,簡稱 3PC)是分佈式事務處理中的一種算法,是在兩階段提交(2PC)的基礎上進一步發展的,主要是爲了解決 2PC 中存在的一些問題,比如資源佔用、同步阻塞、單點故障以及提交階段可能出現的數據不一致問題。

相對於 2PC,3PC 有兩個變動的地方:

三階段過程

3 階段提交的具體過程如下:

第一階段:準備階段(CanCommit)

事務協調者向所有參與者發送準備請求,並詢問參與者是否可以提交事務,並開始等待各參與者響應。參與者接收到請求後,會查詢本地資源是否可以提交,並返回查詢結果,而不需要對資源進行實際的修改操作。如果查詢資源可以提交,則回覆事務管理器 “可以提交(Yes)”,否則回覆 “不可以提交(No)”。

和 2PC 的第一階段不同的是,這裏只執行檢查,並不鎖定資源。

第二階段:預提交階段(PreCommit)

協調者根據第一階段參與者返回的結果,會出現兩種情況:執行事務預提交和中斷事務。

情況一,執行事務預提交:

情況二,中斷事務:

第三階段:確認階段(DoCommit)

根據第二階段的結果,也可以分爲兩種情況,提交事務和中斷事務。

情況一,提交事務:

情況二,中斷事務

解決了 2PC 哪些問題

3PC 解決了 2PC 存在的以下問題:

  1. 單點故障:3PC 引入了一個準備階段,這樣即使協調者在第一階段失敗,參與者也可以在第二階段中完成提交或回滾,並且如果協調者故障參與者會自動提交,不需要一直等待。

  2. 性能問題:在 3PC 中,可以預先檢查參與者狀態,減少鎖定資源的情況,提高系統的性能。

  3. 阻塞問題:3PC 中通過在第一階段中引入超時機制,避免了協調者一直等待的問題。如果協調者在一定時間內沒有收到所有參與者的響應,就會繼續進行第三階段的操作,從而避免了事務的阻塞問題。

  4. 部分解決數據不一致問題:在 3PC 中,如果在第二階段中協調者發生故障,參與者會繼續等待協調者的恢復。如果協調者無法恢復,參與者會在一定時間內自行決定提交或回滾。這樣可以部分避免 2PC 中的數據不一致問題。

只能說 3PC 對 2PC 存在的一些問題有改善,但沒有徹底解決。

比如它依舊存在一致性風險問題,並且風險反而略有增加。進入 PreCommit 階段之後,協調者發出的指令不是 Ack 而是 Abort,而此時因網絡問題,有部分參與者直至超時都未能收到協調者的 Abort 指令的話,這些參與者將會錯誤地提交事務,這就產生了不同參與者之間數據不一致的問題。

由於 3PC 非常難實現,目前市面上主流的分佈式事務解決方案都是 2PC 協議。所以,在實際應用中需要根據具體的場景和需求,選擇適合的分佈式事務協議。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/Ju4-RQJVMO-kr4gnC8uIkg