分佈式系統事務一致性

一 分佈式系統特色

現今互聯網界,分佈式系統和微服務架構盛行。業界著名的CAP理論也告訴咱們,在設計和實現一個分佈式系統時,須要將數據一致性、系統可用性和分區容忍性放在一塊兒考慮。算法

一、CAP理論數據庫

在分佈式系統中,一致性(Consistency)、可用性(Availability)和分區容忍性(Partition Tolerance)3 個要素最多隻能同時知足兩個,不可兼得。其中,分區容忍性又是不可或缺的。網絡

圖片

  • 一致性:分佈式環境下多個節點的數據是否強一致。架構

  • 可用性:分佈式服務能一直保證可用狀態。當用戶發出一個請求後,服務能在有限時間內返回結果。併發

  • 分區容忍性:特指對網絡分區的容忍性。app

舉例:Cassandra、Dynamo 等,默認優先選擇AP,弱化C;HBase、MongoDB 等,默認優先選擇CP,弱化A。異步

二、BASE 理論分佈式

核心思想:ide

  • 基本可用(Basically Available):指分佈式系統在出現故障時,容許損失部分的可用性來保證核心可用。微服務

  • 軟狀態(Soft State):指容許分佈式系統存在中間狀態,該中間狀態不會影響到系統的總體可用性。

  • 最終一致性(Eventual Consistency):指分佈式系統中的全部副本數據通過必定時間後,最終可以達到一致的狀態。

二 一致性模型

數據的一致性模型能夠分紅如下 3 類:

  1. 強一致性:數據更新成功後,任意時刻全部副本中的數據都是一致的,通常採用同步的方式實現。

  2. 弱一致性:數據更新成功後,系統不承諾當即能夠讀到最新寫入的值,也不承諾具體多久以後能夠讀到。

  3. 最終一致性:弱一致性的一種形式,數據更新成功後,系統不承諾當即能夠返回最新寫入的值,可是保證最終會返回上一次更新操做的值。

分佈式系統數據的強一致性、弱一致性和最終一致性能夠經過Quorum NRW算法分析。

三 分佈式事務

分佈式事務的目的是保障分佈式存儲中數據一致性,而跨庫事務會遇到各類不可控制的問題,如個別節點宕機,像單機事務同樣的ACID是沒法奢望的。一、Two/Three Phase Commit2PC,中文叫兩階段提交。在分佈式系統中,每一個節點雖然能夠知曉本身的操做時成功或者失敗,卻沒法知道其餘節點的操做的成功或失敗。當一個事務跨越多個節點時,爲了保持事務的ACID特性,須要引入一個做爲協調者的組件來統一掌控全部節點(稱做參與者)的操做結果並最終指示這些節點是否要把操做結果進行真正的提交。兩階段提交的算法以下:第一階段:

  1. 協調者會問全部的參與者結點,是否能夠執行提交操做。
  2. 各個參與者開始事務執行的準備工做:如:爲資源上鎖,預留資源。
  3. 參與者響應協調者,若是事務的準備工做成功,則迴應「能夠提交」,不然迴應「拒絕提交」。

第二階段:

  • 若是全部的參與者都回應「能夠提交」,那麼,協調者向全部的參與者發送「正式提交」的命令。參與者完成正式提交,並釋放全部資源,而後迴應「完成」,協調者收集各結點的「完成」迴應後結束這個Global Transaction。
  • 若是有一個參與者迴應「拒絕提交」,那麼,協調者向全部的參與者發送「回滾操做」,並釋放全部資源,而後迴應「回滾完成」,協調者收集各結點的「回滾」迴應後,取消這個Global Transaction。

兩段提交最大的問題就是第3)項,若是第一階段完成後,參與者在第二階沒有收到決策,那麼數據結點會進入「不知所措」的狀態,這個狀態會block住整個事務。也就是說,協調者Coordinator對於事務的完成很是重要,Coordinator的可用性是個關鍵。因些,咱們引入三段提交,三段提交在Wikipedia上的描述以下,他把二段提交的第一個段break成了兩段:詢問,而後再鎖資源。最後真正提交。三段提交的核心理念是:在詢問的時候並不鎖定資源,除非全部人都贊成了,纔開始鎖資源。但三階段提交也存在一些缺陷,要完全從協議層面避免數據不一致,能夠採用Paxos或者Raft 算法。目前兩階段提交、三階段提交存在以下的侷限性,並不適合在微服務架構體系下使用:

  • 全部的操做必須是事務性資源(好比數據庫、消息隊列、EJB組件等),存在使用侷限性(微服務架構下多數使用HTTP協議),比較適合傳統的單體應用;
  • 因爲是強一致性,資源須要在事務內部等待,性能影響較大,吞吐率不高,不適合高併發與高性能的業務場景;

二、Try Confirm Cancel(TCC)

一個完整的TCC業務由一個主業務服務和若干個從業務服務組成,主業務服務發起並完成整個業務活動,TCC模式要求從服務提供三個接口:Try、Confirm、Cancel。

  1. Try:完成全部業務檢查,預留必須業務資源。

  2. Confirm:真正執行業務,不做任何業務檢查;只使用Try階段預留的業務資源;Confirm操做知足冪等性。

  3. Cancel:釋放Try階段預留的業務資源;Cancel操做知足冪等性。

整個TCC業務分紅兩個階段完成:

第一階段:主業務服務分別調用全部從業務的try操做,並在活動管理器中登記全部從業務服務。當全部從業務服務的try操做都調用成功或者某個從業務服務的try操做失敗,進入第二階段。第二階段:活動管理器根據第一階段的執行結果來執行confirm或cancel操做。若是第一階段全部try操做都成功,則活動管理器調用全部從業務活動的confirm操做。不然調用全部從業務服務的cancel操做。與2PC比較:

  • 位於業務服務層而非資源層。
  • 沒有單獨的準備(prepare)階段,Try操做兼備資源操做與準備能力。
  • Try操做能夠靈活選擇業務資源的鎖定粒度。
  • 開發成本較高。

缺點:

  • Canfirm和Cancel的冪等性很難保證。
  • 這種方式缺點比較多,一般在複雜場景下是不推薦使用的,除非是很是簡單的場景,很是容易提供回滾Cancel,並且依賴的服務也很是少的狀況。
  • 這種實現方式會形成代碼量龐大,耦合性高。並且很是有侷限性,由於有不少的業務是沒法很簡單的實現回滾的,若是串行的服務不少,回滾的成本實在過高。

三、基於消息的分佈式事務

核心思想:

eBay 的架構師Dan Pritchett,曾在一篇解釋BASE 原理的論文《Base:An Acid Alternative》中提到一個eBay 分佈式系統一致性問題的解決方案。它的核心思想是將須要分佈式處理的任務經過消息或者日誌的方式來異步執行,消息或日誌能夠存到本地文件、數據庫或消息隊列,再經過業務規則進行失敗重試,它要求各服務的接口是冪等的。

基於消息的分佈式事務模式核心思想是經過消息系統來通知其餘事務參與方本身事務的執行狀態。消息系統的引入更有效的將事務參與方解耦,各個參與方能夠異步執行。

該種模式的難點在於解決本地事務執行和消息發送的一致性:二者要同時執行成功或者同時取消執行。

實現上主要有兩種方式:

  • 基於事務消息的方案

  • 基於本地消息的方案

1)基於事務消息的分佈式事務普通消息是沒法解決本地事務執行和消息發送的一致性問題的。由於消息發送是一個網絡通訊的過程,發送消息的過程就有可能出現發送失敗、或者超時的狀況。超時有可能發送成功了,有可能發送失敗了,消息的發送方是沒法肯定的,因此此時消息發送方不管是提交事務仍是回滾事務,都有可能不一致性出現。解決這個問題,須要引入事務消息,事務消息和普通消息的區別在於事務消息發送成功後,處於 prepared 狀態,不能被訂閱者消費,等到事務消息的狀態更改成可消費狀態後,下游訂閱者才能夠監聽到次消息。本地事務和事務消息的發送的處理流程以下:

  • 事務發起者預先發送一個事務消息。
  • MQ 系統收到事務消息後,將消息持久化,消息的狀態是「待發送」,並給發送者一個 ACK 消息。
  • 事務發起者若是沒有收到 ACK 消息,則取消本地事務的執行;若是收到了 ACK 消息,則執行本地事務,並給 MQ 系統再發送一個消息,通知本地事務的執行狀況。
  • MQ 系統收到消息通知後,根據本地事務的執行狀況更改事務消息的狀態,若是成功執行,則將消息更改成「可消費」並擇機下發給訂閱者;若是事務執行失敗,則刪除該事務消息。
  • 本地事務執行完畢後,發給 MQ 的通知消息有可能丟失了。因此支持事務消息的 MQ 系統有一個定時掃描邏輯,掃描出狀態仍然是「待發送」狀態的消息,並向消息的發送方發起詢問,詢問這條事務消息的最終狀態如何並根據結果更新事務消息的狀態。所以事務的發起方須要給 MQ 系統提供一個事務消息狀態查詢接口。
  • 若是事務消息的狀態是「可發送」,則 MQ 系統向下遊參與者推送消息,推送失敗會不停重試。
  • 下游參與者收到消息後,執行本地事務,本地事務若是執行成功,則給 MQ 系統發送 ACK 消息;若是執行失敗,則不發送 ACK 消息,MQ 系統會持續推送給消息。

圖片

2)基於本地消息的分佈式事務

基於事務消息的模式對 MQ 系統要求較高,並非全部 MQ 系統都支持事務消息的,RocketMQ 是目前爲數很少的支持事務消息的 MQ 系統。若是所依賴的 MQ 系統不支持事務消息,那麼能夠採用本地消息的分佈式模式。該種模式的核心思想是:上游服務:

  • 事務的發起方維護一個本地消息表,業務執行和本地消息表的執行處在同一個本地事務中。業務執行成功,則同時記錄一條「待發送」狀態的消息到本地消息表中。
  • 系統中啓動一個定時任務定時掃描本地消息表中狀態爲「待發送」的記錄,並將其發送到 MQ 系統中,若是發送失敗或者超時,則一直髮送,直到發送成功後,從本地消息表中刪除該記錄(或修改狀態爲「已發送」)。
  • 消息會重試發送,可能會重複,因此每條消息須要一個惟一ID。

下游服務:

  • 後續的消息訂閱者從MQ消費消息,進行下游的本地事務操做。
  • 爲了不消息重複消費,下游服務能夠維護一個本地的「消息記錄表」記錄已經處理消費過的消息,每次處理消息前經過該表檢查消息是否消費過。

圖片

基於消息的分佈式事務能夠將分佈式系統之間更有效的解耦,各個事務參與方之間的調用再也不是同步調用。

對MQ系統的要求較高,對業務實現也有必定的侵入性,要麼提供事務消息狀態查詢接口,要麼須要維護本地消息表。而且原則上只接受下游分支事務的成功,不接受事務的回滾,若是失敗就要一直重試,適用於對最終一致性敏感度較低的業務場景,例如跨企業的系統間的調用,適用的場景有限。

總結

閱讀了很多這方面的文章,在此基礎上,總結一下分佈式事務一致性的解決方案。分佈式系統的事務一致性自己就是一個技術難題,目前沒有一種很簡單很完美的方案可以應對全部場景。分佈式系統的一個難點就是由於「網絡通訊的不可靠」,只能經過「確認機制」、「重試機制」、「補償機制」等各方面來解決問題。在綜合考慮可用性、性能、實現複雜度等各方面的狀況上,比較好的選擇是「異步消息確保最終一致性」,只是具體實現方式上有一些差別。

相關文章
相關標籤/搜索