1. 程式人生 > >微服務架構下處理分散式事務,你必須知道的事兒

微服務架構下處理分散式事務,你必須知道的事兒

根據微服務架構的鼻祖 Martin Fowler 的忠告,微服務架構中應當儘量避免分散式事務。然而,在某些領域,分散式事務如同宿命中的對手無法避免。

在工程領域,分散式事務的討論主要聚焦於強一致性和最終一致性的解決方案。

典型方案包括:

  • 兩階段提交(2PC, Two-phase Commit)方案。
  • eBay 事件佇列方案。
  • TCC 補償模式。
  • 快取資料最終一致性。

一致性理論

分散式事務的目的是保障分庫資料一致性,而跨庫事務會遇到各種不可控制的問題,如個別節點永久性宕機,像單機事務一樣的 ACID 是無法奢望的。

另外,業界著名的 CAP 理論也告訴我們,對分散式系統,需要將資料一致性和系統可用性、分割槽容忍性放在天平上一起考慮。

兩階段提交協議(簡稱2PC)是實現分散式事務較為經典的方案,但 2PC 的可擴充套件性很差,在分散式架構下應用代價較大,eBay 架構師 Dan Pritchett 提出了 BASE 理論,用於解決大規模分散式系統下的資料一致性問題。

BASE 理論告訴我們:可以通過放棄系統在每個時刻的強一致性來換取系統的可擴充套件性。

01.CAP 理論

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

                         

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

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

02.BASE 理論

核心思想:

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

一致性模型

資料的一致性模型可以分成以下三類:

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

分散式系統資料的強一致性、弱一致性和最終一致性可以通過 Quorum NRW 演算法分析。

分散式事務解決方案

01.2PC 方案——強一致性

2PC 的核心原理是通過提交分階段和記日誌的方式,記錄下事務提交所處的階段狀態,在元件宕機重啟後,可通過日誌恢復事務提交的階段狀態,並在這個狀態節點重試。

如 Coordinator 重啟後,通過日誌可以確定提交處於 Prepare 還是 Prepare All 狀態。若是前者,說明有節點可能沒有 Prepare 成功,或所有節點 Prepare 成功但還沒有下發 Commit,狀態恢復後給所有節點下發 RollBack。

若是 Prepare All 狀態,需要給所有節點下發 Commit,資料庫節點需要保證 Commit 冪等。

                       

2PC 方案的三個問題:

  • 同步阻塞。
  • 資料不一致。
  • 單點問題。

升級的 3PC 方案旨在解決這些問題,主要有兩個改進:

  • 增加超時機制。
  • 兩階段之間插入準備階段。

但三階段提交也存在一些缺陷,要徹底從協議層面避免資料不一致,可以採用 Paxos 或者 Raft 演算法。

02.eBay 事件佇列方案——最終一致性

eBay 的架構師 Dan Pritchett,曾在一篇解釋 BASE 原理的論文《Base:An Acid Alternative》中提到一個 eBay 分散式系統一致性問題的解決方案。

它的核心思想是將需要分散式處理的任務通過訊息或者日誌的方式來非同步執行,訊息或日誌可以存到本地檔案、資料庫或訊息佇列,再通過業務規則進行失敗重試,它要求各服務的介面是冪等的。

描述的場景為,有使用者表 user 和交易表 transaction,使用者表儲存使用者資訊、總銷售額和總購買額。交易表儲存每一筆交易的流水號、買家資訊、賣家資訊和交易金額。如果產生了一筆交易,需要在交易表增加記錄,同時還要修改使用者表的金額。

                       

論文中提出的解決方法是將更新交易表記錄和使用者表更新訊息放在一個本地事務來完成,為了避免重複消費使用者表更新訊息帶來的問題,增加一個操作記錄表 updates_applied 來記錄已經完成的交易相關的資訊。

                       

這個方案的核心在於第二階段的重試和冪等執行。失敗後重試,這是一種補償機制,它是能保證系統最終一致的關鍵流程。

03.TCC (Try-Confirm-Cancel)補償模式——最終一致性

某業務模型如圖,由服務 A、服務 B、服務 C、服務 D 共同組成的一個微服務架構系統。服務 A 需要依次呼叫服務 B、服務 C 和服務 D 共同完成一個操作。

當服務 A 呼叫服務 D 失敗時,若要保證整個系統資料的一致性,就要對服務 B 和服務 C 的 invoke 操作進行回滾,執行反向的 revert 操作。回滾成功後,整個微服務系統是資料一致的。

                        

實現的三個關鍵要素:

  • 服務呼叫鏈必須被記錄下來。
  • 每個服務提供者都需要提供一組業務邏輯相反的操作,互為補償,同時回滾操作要保證冪等。
  • 必須按失敗原因執行不同的回滾策略。

實現的兩個難點:

  • 補償模式的特點是實現簡單,但是想形成一定程度的通用方案比較困難,特別是服務鏈的記錄,因為大部分時候,業務引數或者業務邏輯千差萬別。
  • 很多業務特徵使得該服務無法提供一個安全的回滾操作。

04.快取資料最終一致性

在我們的業務系統中,快取(Redis 或者 Memcached)通常被用在資料庫前面,作為資料讀取的緩衝,使得 I/O 操作不至於直接落在資料庫上。

以商品詳情頁為例,假如賣家修改了商品資訊,並寫回到資料庫,但是這時候使用者從商品詳情頁看到的資訊還是從快取中拿到的過時資料,這就出現了快取系統和資料庫系統中的資料不一致的現象。

要解決該場景下快取和資料庫資料不一致的問題,我們有以下兩種解決方案:

  • 為快取資料設定過期時間。當快取中資料過期後,業務系統會從資料庫中獲取資料,並將新值放入快取。這個過期時間就是系統可以達到最終一致的容忍時間。
  • 更新資料庫資料後,同時清除快取資料。資料庫資料更新後,同步刪除快取中資料,使得下次對商品詳情的獲取直接從資料庫中獲取,並同步到快取。

選擇建議

在面臨資料一致性問題的時候,首先要從業務需求的角度出發,確定我們對於三種一致性模型的接受程度,再通過具體場景來決定解決方案。

從應用角度看,分散式事務的現實場景常常無法規避,在有能力給出其他解決方案前,2PC 也是一個不錯的選擇。

對購物轉賬等電商和金融業務,中介軟體層的 2PC 最大問題在於業務不可見,一旦出現不可抗力或意想不到的一致性破壞。

如資料節點永久性宕機,業務難以根據 2PC 的日誌進行補償。金融場景下,資料一致性是命根,業務需要對資料有百分之百的掌控力。

建議使用 TCC 這類分散式事務模型,或基於訊息佇列的柔性事務框架,這兩種方案都在業務層實現,業務開發者具有足夠掌控力,可以結合 SOA 框架來架構,包括 Dubbo、Spring Cloud 等。