天天看點

分布式事務:兩階段送出與三階段送出

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

  • C:一緻性

在分布式系統中資料往往存在多個副本,一緻性描述的是這些副本中的資料在内容群組織上的一緻。

  • A:可用性

可用性描述了系統對使用者的服務能力,所謂可用是指在使用者容忍的時間範圍内傳回使用者期望的結果。

  • P:分區容錯性

分布式系統通常由多個節點構成,由于網絡是不可靠的,是以存在分布式叢集中的節點因為網絡通信故障導緻被孤立成一個個小叢集的可能性,即網絡分區,分區容錯性要求在出現網絡分區時系統仍然能夠對外提供一緻性的可用服務。

對于一個分布式系統而言,我們要始終假設網絡是不可靠的,是以分區容錯性是對一個分布式系統最基本的要求,我們的切入點更多的是嘗試在可用性和一緻性之間尋找一個平衡點,但這也并非要求我們在系統設計時一直建立在網絡出現分區的場景之上,然後對一緻性和可用性在選擇時非此即彼。實際上 Eric Brewer 在 2012 年就曾指出 CAP 理論證明不能同時滿足一緻性、可用性,以及分區容錯性的觀點在實際系統設計指導上存在一定的誤導性。傳統對于 CAP 理論的了解認為在設計分布式系統時必須滿足 P,然後在 C 和 A 之間進行取舍,這是片面的,實際中網絡出現分區的可能性還是比較小的,尤其是目前網絡環境正在變得越來越好,甚至許多系統都擁有專線支援,是以在網絡未出現分區時,還是應該兼顧 A 和 C;另外就是對于一緻性、可用性,以及分區容錯性三者在度量上也應該有一個評定範圍,最簡單的以可用性來說,當有多少占比請求出現響應逾時才可以被認為是不滿足可用性,而不是一出現逾時就認為是不可用的;最後我們需要考慮的一點就是分布式系統一般都是一個比較大且複雜的系統,我們應該從更小的粒度上對各個子系統進行評估和設計,而不是簡單的從整體上認為需要滿足 P,而在 A 和 C 之間做取舍,一些子系統可能需要盡可能同時滿足三者。

讓分布式叢集始終對外提供可用的一緻性服務一直是富有挑戰和趣味的一項任務。暫且抛開可用性,拿一緻性來說,對于關系型資料庫我們通常利用事務來保證資料的強一緻性,當我們的資料量越來越大,大到單庫已經無法承擔時,我們不得不采取分庫分表的政策對資料庫實作水準拆分,建構分布式資料庫叢集,這樣可以将一個資料庫的壓力分攤到多個資料庫,極大的提升了資料庫的存儲和響應能力,但是拆分之後也為我們使用資料庫帶來了許多的限制,比如主鍵的全局唯一、聯表查詢、資料聚合等等,另外一個相當棘手的問題就是資料庫的事務由原先的單庫事務變成了現在的分布式事務。

分布式事務的實作并不是很難,比如下文要展開的兩階段送出(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. 一個或多個參與者執行事務失敗

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

  1. 協調者向所有參與者發送事務commit通知
  2. 所有參與者在收到通知之後執行commit操作,并釋放占有的資源
  3. 參與者向協調者回報事務送出結果
分布式事務:兩階段送出與三階段送出

針對第二、三種情況,協調者認為事務無法正常執行,于是向各個參與者發送事務復原請求,具體步驟如下:

  1. 協調者向所有參與者發送事務rollback通知
  2. 所有參與者在收到通知之後執行rollback操作,并釋放占有的資源

繼續閱讀