分布式系統事務一致性


一 分布式系統特點

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

1、CAP理論

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

  • 一致性:分布式環境下多個節點的數據是否強一致。
  • 可用性:分布式服務能一直保證可用狀態。當用戶發出一個請求后,服務能在有限時間內返回結果。
  • 分區容忍性:特指對網絡分區的容忍性。

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

2、BASE 理論

核心思想:

  • 基本可用(Basically Available):指分布式系統在出現故障時,允許損失部分的可用性來保證核心可用。
  • 軟狀態(Soft State):指允許分布式系統存在中間狀態,該中間狀態不會影響到系統的整體可用性。
  • 最終一致性(Eventual Consistency):指分布式系統中的所有副本數據經過一定時間后,最終能夠達到一致的狀態。

二 一致性模型

數據的一致性模型可以分成以下 3 類:

  1. 強一致性:數據更新成功后,任意時刻所有副本中的數據都是一致的,一般采用同步的方式實現。
  2. 弱一致性:數據更新成功后,系統不承諾立即可以讀到最新寫入的值,也不承諾具體多久之后可以讀到。
  3. 最終一致性:弱一致性的一種形式,數據更新成功后,系統不承諾立即可以返回最新寫入的值,但是保證最終會返回上一次更新操作的值。

分布式系統數據的強一致性、弱一致性和最終一致性可以通過Quorum NRW算法分析。

三 分布式事務

分布式事務的目的是保障分布式存儲中數據一致性,而跨庫事務會遇到各種不可控制的問題,如個別節點宕機,像單機事務一樣的ACID是無法奢望的。

1、Two/Three Phase Commit

2PC,中文叫兩階段提交。在分布式系統中,每個節點雖然可以知曉自己的操作時成功或者失敗,卻無法知道其他節點的操作的成功或失敗。當一個事務跨越多個節點時,為了保持事務的ACID特性,需要引入一個作為協調者的組件來統一掌控所有節點(稱作參與者)的操作結果並最終指示這些節點是否要把操作結果進行真正的提交。 兩階段提交的算法如下:

第一階段:

  1. 協調者會問所有的參與者結點,是否可以執行提交操作。
  2. 各個參與者開始事務執行的准備工作:如:為資源上鎖,預留資源。
  3. 參與者響應協調者,如果事務的准備工作成功,則回應“可以提交”,否則回應“拒絕提交”。

第二階段:

  • 如果所有的參與者都回應“可以提交”,那么,協調者向所有的參與者發送“正式提交”的命令。參與者完成正式提交,並釋放所有資源,然后回應“完成”,協調者收集各結點的“完成”回應后結束這個Global Transaction。
  • 如果有一個參與者回應“拒絕提交”,那么,協調者向所有的參與者發送“回滾操作”,並釋放所有資源,然后回應“回滾完成”,協調者收集各結點的“回滾”回應后,取消這個Global Transaction。

兩段提交最大的問題就是第3)項,如果第一階段完成后,參與者在第二階沒有收到決策,那么數據結點會進入“不知所措”的狀態,這個狀態會block住整個事務。也就是說,協調者Coordinator對於事務的完成非常重要,Coordinator的可用性是個關鍵。

因些,我們引入三段提交,三段提交在Wikipedia上的描述如下,他把二段提交的第一個段break成了兩段:詢問,然后再鎖資源。最后真正提交。三段提交的核心理念是:在詢問的時候並不鎖定資源,除非所有人都同意了,才開始鎖資源。但三階段提交也存在一些缺陷,要徹底從協議層面避免數據不一致,可以采用Paxos或者Raft 算法

目前兩階段提交、三階段提交存在如下的局限性,並不適合在微服務架構體系下使用:

  • 所有的操作必須是事務性資源(比如數據庫、消息隊列、EJB組件等),存在使用局限性(微服務架構下多數使用HTTP協議),比較適合傳統的單體應用;

  • 由於是強一致性,資源需要在事務內部等待,性能影響較大,吞吐率不高,不適合高並發與高性能的業務場景;

2、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,而且依賴的服務也非常少的情況。
  • 這種實現方式會造成代碼量龐大,耦合性高。而且非常有局限性,因為有很多的業務是無法很簡單的實現回滾的,如果串行的服務很多,回滾的成本實在太高。

3、基於消息的分布式事務

核心思想:

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

總結

閱讀了不少這方面的文章,在此基礎上,總結一下分布式事務一致性的解決方案。分布式系統的事務一致性本身就是一個技術難題,目前沒有一種很簡單很完美的方案能夠應對所有場景。分布式系統的一個難點就是因為“網絡通信的不可靠”,只能通過“確認機制”、“重試機制”、“補償機制”等各方面來解決問題。在綜合考慮可用性、性能、實現復雜度等各方面的情況上,比較好的選擇是“異步消息確保最終一致性”,只是具體實現方式上有一些差異。

 

參考:

分布式系統的事務處理

分布式系統事務一致性解決方案

理性撕逼!分布式事務:不過是在一致性、吞吐量和復雜度之間,做一個選擇

知乎:常用的分布式事務解決方案介紹有多少種?

一次給女朋友轉賬引發我對分布式事務的思考

用消息隊列和消息應用狀態表來消除分布式事務

程立:《大規模SOA系統中的分布事務處事》

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM