漫談分佈式事務的那些解決方案

事務咱們都不陌生,咱們常說的事務通常都是指單機事務,即本地事務。那分佈式事務是什麼?分佈式事務就是由多個本地事務組合而成的事務,通常在分佈式場景下才會出現。程序員

好比電商平臺中,咱們在購物的時候,下單支付這個過程看上去是一鼓作氣的,可是背後多是多個系統的分工合做。訂單系統、支付系統、物流系統等。這些系統部署在不一樣的服務器上,執行的都是各類的事務,對於電商平臺來講,這就是分佈式事務。算法

本地事務都好解決,有一套現成的事務機制,分佈式事務比本地事務就要複雜多。如何實現分佈式事務呢?大概有 3 種解決方式:數據庫

  • 基於 XA 協議的二階段提交協議
  • 三階段提交協議
  • 基於 MQ 的最終一致性

基於 XA 協議的二階段提交

XA協議由 Tuxedo 首先提出的,並交給X/Open組織,做爲資源管理器(數據庫)與事務管理器的接口標準。目前,Oracle、Informix、DB2和Sybase等各大數據庫廠家都提供對XA的支持。----來源百度百科服務器

二階段提交也叫 2PC ,The two-phase commit protocol。首先在二階段提交中有兩個角色:網絡

  • 參與者:本地資源管理器,即事務的執行者,也就是各個業務系統。
  • 協調者:分佈式事務的大腦,負責指揮協調各個業務系統提交/回滾事務。

所謂得兩階段提交就是指投票(voting)和提交(commit) 兩個階段,跟選舉制度同樣,先投票,再決定。異步

投票階段,協調者向參與者發起執行事務操做的請求(CanCommit 請求),並等待參與者響應。分佈式

參與者接受到請求後,執行事務請求操做,記錄日誌信息但不提交,記錄成功後,向協調者發生 「Yes」 消息,表示贊成提交操做,若不成功,則發送「No」 消息,表示不一樣意此次操做。注意這個過程會鎖定數據。性能

投票階段得流程圖,大概就是下面這樣子:大數據

二階段提交投票階段

提交階段,協調者接受到全部參與者的響應以後,根據返回來的信息狀況,向參與者發送提交或回滾請求。日誌

  • 若收到的響應消息都是 「Yes」,則向參與者發送 「DoCommit」 消息,參與者完成本地事務的其餘操做並釋放資源,而後向協調者發送 「HaveCommitted」消息;

  • 若是協調者收到的消息中包含「No」消息或者在規定時間內有參與者沒有響應,則向全部參與者發送「DoAbort」消息,此時發送「Yes」的參與者則會根據以前執行操做時的回滾日誌對操做進行回滾,而後全部參與者會向協調者發送「HaveCommitted」消息;

提交階段的流程,大概入下圖所示:

二階段提交提交階段

二階段提交協議容易理解,基於 XA 的二階段提交算法知足事務的 ACID 特性,看上去比較完美,可是缺點仍是挺多的,主要有如下幾個問題:

  • 同步阻塞問題:二階段提交在執行過程當中,全部參與節點都是事務阻塞型的,參與者會鎖定數據,其餘訪問者要訪問該數據的話,都會被阻塞。
  • 單點故障問題。在二階段提交協議中,協調者只有一臺,一旦協調者發送故障,整個系統都會處於停滯階段。特別是提交階段,若是協調者掛了的話,參與者就會一直等待協調者回應,會處於阻塞中。
  • 數據不一致問題:在提交階段,協調者向參與者發送 DoCommit 請求後,因爲網絡抖動或者在發送請求的過程當中,協調者發生故障,就會致使只有一部分參與者接收到了提交請求並執行提交操做,但其餘未接到提交請求的那部分參與者則沒法執行事務提交。因而整個分佈式系統便出現了數據不一致的問題。

三階段提交

三階段提交協議(Three-phase commit protocol,3PC)是對二階段提交(2PC)的改進。解決了二階段提交的一些問題,三階段和二階段提交最大的不一樣是引入超時機制和準備階段

先來講說超時機制,在二階段提交,只有協調者纔有超時機制,若是協調者在規定時間內沒有接收到參與者的響應,就會根據當前狀態提交或者終止整個事務,可是若是協調者掛了,參與者並無超時機制,因此就一直等待,這也是二階段提交單點故障的問題。在三階段提交中,同時在協調者和參與者中引入超時機制。若是協調者或參與者在規定的時間內沒有接收到來自其餘節點的響應,就會根據當前的狀態選擇提交或者終止整個事務。

三階段提交其實就是將二階段提交中的提交階段一分爲二,三階段提交協議中的具體三階段是:CanCommit、PreCommit、DoCommit 三個階段

CanCommit 階段,CanCommit 階段與 2PC 的投票階段相似:協調者向參與者發送請求操做(CanCommit 請求),詢問參與者是否能夠執行事務提交操做,而後等待參與者的響應;參與者收到 CanCommit 請求以後,回覆 Yes,表示能夠順利執行事務;不然回覆 No。

PreCommit 階段,根據二階段提交中的提交階段類似,根據 CanCommit 階段返回的結果,來決定是否能夠進行 PreCommit 操做。

圖片描述

這時候就存在兩種狀況,若是全部參與者都回覆 「Yes」,那麼執行流程是這樣的:

  • 一、協調者發送預提交請求:協調者向參與者發送 PreCommit 請求,進入預提交階段.
  • 二、事務預提交:參與者接收到 PreCommit 請求後執行事務操做,並將 Undo 和 Redo 信息記錄到事務日誌中。
  • 三、響應反饋:若是參與者成功執行了事務操做,則返回 ACK 響應,同時開始等待最終指令。

若是有參與者返回 「No」,或者協調者在規定時間內沒有收到參與者的響應,那麼將執行中斷事務操做。流程是這樣的:

  • 一、發送中斷請求:協調者向全部參與者發送「Abort」消息。
  • 二、中斷事務:參與者收到「Abort」消息以後,或超時後仍未收到協調者的消息,執行事務的中斷操做。

DoCommit 階段, 事務真正提交階段,協調者根據 PreCommit 階段參與者返回來的信息,決定是進入提交階段仍是事務中斷階段。

圖片描述

提交階段流程以下:

  • 一、發送提交請求:協調者接收到全部參與者發送的 Ack 響應,從預提交狀態進入到提交狀態,並向全部參與者發送 DoCommit 消息。
  • 二、事務提交:參與者接收到 DoCommit 消息以後,正式提交事務。完成事務提交以後,釋放全部鎖住的資源。
  • 三、響應反饋:參與者提交完事務以後,向協調者發送 Ack 響應。
  • 四、完成事務:協調者接收到全部參與者的 Ack 響應以後,完成事務。

事務中斷階段,流程以下:

  • 一、發送中斷請求:協調者向全部參與者發送 Abort 請求。
  • 二、事務回滾:參與者接收到 Abort 消息以後,利用其在 PreCommit 階段記錄的 Undo 信息執行事務的回滾操做,並釋放全部鎖住的資源。
  • 三、反饋結果:參與者完成事務回滾以後,向協調者發送 Ack 消息。
  • 四、中斷事務:協調者接收到參與者反饋的 Ack 消息以後,執行事務的中斷,並結束事務。

基於分佈式消息的最終一致性方案

不論是二階段提交仍是三階段提交,都屬於強一致性的,知足事務的 ACID 原則。它們都有兩個共同的問題:

  • 一、須要鎖定數據,下降了系統性能
  • 二、由於網絡等緣由,並無徹底解決數據一致性問題。

而基於 MQ 消息的分佈式解決方案就不太同樣了,它採用的不是強一致性,而是最終一致性,這也就是 BASE 理論。而且咱們直到 MQ 是異步的,因此性能也比較快,能夠說完美的解決了上面兩種方式帶來的問題。

基於 MQ 消息中間件解決分佈式事務的思路是這樣的:主要是基於 MQ 消息投遞的可靠性,將分佈式事務發送給 MQ 中間件以後,中間件將事務持久化,這一點很是重要,保證消息不丟失。消費者端異步消費,若是遇到失敗狀況,因爲咱們的消息是持久化的,因此能夠根據業務規則不斷重試,有必要的話,人工補償,保證數據最終一致性。

關於基於分佈式消息的最終一致性方案,我準備基於 RocketMQ 單獨開一個章節,詳細聊一聊,這裏就很少說了。

歡迎關注公衆號【互聯網平頭哥】。關注這個互聯網苟且偷生的程序員,願你我共同進步,今天最好的是明天最低的要求。

互聯網平頭哥
相關文章
相關標籤/搜索