SpringCloud 分佈式事務

在分佈式系統中,分佈式事務基本上是繞不開的, 分佈式事務是指事務的參與者、支持事務的服務器、資源服務器以及事務管理器分別位於不同的分佈式系統的不同節點之上 。其實就可以簡單理解成在分佈式系統中實現事務。

一個簡單的例子,電商系統中,下單接口,一般會有扣庫存,扣積分,然後生成訂單。而一般來說,這三個系統都是不同的服務,我們本地不能控制其他服務的事務,此時如果訂單服務發生了錯誤進行了回滾,但遠程的服務,如扣庫存已經調用完成,不能進行回滾了。也就是說下單接口的成功與否,不僅取決於本地的 db 操作,而且依賴第三方系統的結果, 這時候分佈式事務就保證這些操作要麼全部成功,要麼全部失敗。本質上來說,分佈式事務就是爲了保證不同數據庫的數據一致性。

在講分佈式事務之前,先回顧下本地事務的知識點。

一、本地事務

嚴格意義上的事務實現應該是具備原子性( Atomicity )、一致性 (Consistency)、隔離性( Isolation ) 和持久性(Durability),簡稱 ACID。

通俗意義上事務就是爲了使得一些更新操作要麼都成功,要麼都失敗。

隔離性中還有一個隔離級別的概念,總共有 4 個事務隔離級別,不同的隔離級別對事務的處理不同,分別是:未提交讀,已提交讀, 可重複讀,串行化。這裏面又牽扯到三個概念,髒讀、不可重複讀 ,幻讀,我們先理解這三個概念

而事務的隔離級別其實就是如何避免這三種

Mysql 默認級別是可重複讀,在編寫代碼時是可以進行設置的。

而在 Spring 中七種事務傳播行爲事務的傳播行爲概念:

這裏有個點,Spring 的事務實現是通過代理類實現的,所以同一個對象內事務調用是默認失效的, 默認只有在外部調用事務纔會生效 。

二、分佈式事務

回顧完本地事務,讓我們回到分佈式事務的學習,分佈式事務的實現是建立在很多概念之上的,讓我們先來理解下基礎概念吧。

2.1 CAP 定理

CAP 是分佈式當中一個非常重要的理論,指的是在一個分佈式系統中一致性 (Consistency)、可用性 (Availability)、分區容錯性(Partition tolerance),三者不可得兼。

CAP 理論是指一個分佈式系統不可能同時滿足一致性,可用性和分區容錯性這個三個基本需求,最多隻能同時滿足其中兩項。

舉個例子:有 A、B、C 三個服務,都保存一份數據是 7,而當 A 服務將這個數據改成 8,同步到 B 時,正常,但同步至 C 時出現了異常,此時 C 仍然是 7,如果此時依舊要保持一致性,那麼 C 服務就不能可用。

大多數時候我們是選擇 AP,也就是選擇放棄一致性,但是這不是絕對的,在一些業務中,例如,轉賬業務,是放棄了可用性。

2.2 BASE 理論

BASE 理論指的是基本可用 Basically Available,軟狀態 Soft State,最終一致性 Eventual Consistency,核心思想是即便無法做到強一致性,但應該採用適合的方式保證最終一致性。

這裏需要解釋下強一致性,弱一致性和最終一致性。

**強一致性:**任何一次讀都能讀到某個數據的最近一次寫的數據。系統中的所有進程,看到的操作順序,都和全局時鐘下的順序一致。簡言之,在任意時刻,所有節點中的數據是一樣的。

弱一致性: 數據更新後,如果能容忍後續的訪問只能訪問到部分或者全部訪問不到,則是弱一致性。

最終一致性: 不保證在任意時刻任意節點上的同一份數據都是相同的,但是隨着時間的遷移,不同節點上的同一份數據總是在向趨同的方向變化。簡單說,就是在一段時間後,節點間的數據會最終達到一致狀態。

三、分佈式事務的幾種方案

上面都是一些理論,用這些理論作爲指導,分佈式事務有以下一些解決方案

3.1 2PC/XA

兩階段提交,顧名思義就是要分兩步提交。存在一個負責協調各個本地資源管理器的事務管理器,本地資源管理器一般是由數據庫實現,事務管理器在第一階段的時候詢問各個資源管理器是否都就緒?如果收到每個資源的回覆都是 yes,則在第二階段提交事務,如果其中任意一個資源的回覆是 no, 則回滾事務。

階段 1:提交事務請求

事務管理器向所有本地資源管理器發起請求,詢問是否是 ready 狀態,所有參與者都將本事務能否成功的信息反饋發給協調者;

二階段:執行事務提交

中斷事務

這種解決方案優點是實現簡單,缺點也很明顯:

**同步阻塞:**當參與事務者存在佔用公共資源的情況,其中一個佔用了資源,其他事務參與者就只能阻塞等待資源釋放,處於阻塞狀態。

**單點故障:**一旦事務管理器出現故障,整個系統不可用

**數據不一致:**在階段二,如果事務管理器只發送了部分 commit 消息,此時網絡發生異常,那麼只有部分參與者接收到 commit 消息,也就是說只有部分參與者提交了事務,使得系統數據不一致。

**不確定性:**當協事務管理器發送 commit 之後,並且此時只有一個參與者收到了 commit,那麼當該參與者與事務管理器同時宕機之後,重新選舉的事務管理器無法確定該條消息是否提交成功。

還有 3PC 提交,是對 2PC 提交做了一些改進

3.2 TCC

TCC 指的是Try - Confirm - Cancel

TCC 事務機制相比於上面介紹的 XA,解決了其幾個缺點:

  1. 解決了協調者單點,由主業務方發起並完成這個業務活動。業務活動管理器也變成多點,引入集羣。

  2. 同步阻塞:引入超時,超時後進行補償,並且不會鎖定整個資源,將資源轉換爲業務邏輯形式,粒度變小。

  3. 數據一致性,有了補償機制之後,由業務活動管理器控制一致性

TCC(Try Confirm Cancel) Try 階段:嘗試執行,完成所有業務檢查(一致性), 預留必須業務資源(準隔離性) Confirm 階段:確認執行真正執行業務,不作任何業務檢查,只使用 Try 階段預留的業務資源,Confirm 操作滿足冪等性。要求具備冪等設計,Confirm 失敗後需要進行重試。Cancel 階段:取消執行,釋放 Try 階段預留的業務資源 Cancel 操作滿足冪等性 Cancel 階段的異常和 Confirm 階段異常處理方案基本上一致。

在 Try 階段,是對業務系統進行檢查及資源預覽,比如訂單和存儲操作,需要檢查庫存剩餘數量是否夠用,並進行預留,預留操作的話就是新建一個可用庫存數量字段,Try 階段操作是對這個可用庫存數量進行操作。 基於 TCC 實現分佈式事務,會將原來只需要一個接口就可以實現的邏輯拆分爲 Try、Confirm、Cancel 三個接口,所以代碼實現複雜度相對較高。

3.3 本地消息表

本地消息表其實就是利用了 各系統本地的事務來實現分佈式事務。

  1. 當系統 A 被其他系統調用發生數據庫表更操作,首先會更新數據庫的業務表,其次會往相同數據庫的消息表中插入一條數據,兩個操作發生在同一個事務中

  2. 系統 A 的腳本定期輪詢本地消息往 mq 中寫入一條消息,如果消息發送失敗會進行重試

  3. 系統 B 消費 mq 中的消息,並處理業務邏輯。如果本地事務處理失敗,會在繼續消費 mq 中的消息進行重試,如果業務上的失敗,可以通知系統 A 進行回滾操作

本地消息表實現的條件:

  1. 消費者與生成者的接口都要支持冪等

  2. 生產者需要額外的創建消息表

  3. 需要提供補償邏輯,如果消費者業務失敗,需要生產者支持回滾操作

容錯機制:

  1. 步驟 1 失敗時,事務直接回滾

  2. 步驟 2、3 寫 mq 與消費 mq 失敗會進行重試

  3. 步驟 3 業務失敗系統 B 向系統 A 發起事務回滾操作

此方案的核心是將需要分佈式處理的任務通過消息日誌的方式來異步執行。消息日誌可以存儲到本地文本、數據庫或消息隊列,再通過業務規則自動或人工發起重試。人工重試更多的是應用於支付場景,通過對賬系統對事後問題的處理。

4.4 可靠消息最終一致性

  1. A 系統先向 mq 發送一條 prepare 消息,如果 prepare 消息發送失敗,則直接取消操作

  2. 如果消息發送成功,則執行本地事務

  3. 如果本地事務執行成功,則想 mq 發送一條 confirm 消息,如果發送失敗,則發送回滾消息

  4. B 系統定期消費 mq 中的 confirm 消息,執行本地事務,併發送 ack 消息。如果 B 系統中的本地事務失敗,會一直不斷重試,如果是業務失敗,會向 A 系統發起回滾請求

  5. mq 會定期輪詢所有 prepared 消息調用系統 A 提供的接口查詢消息的處理情況,如果該 prepare 消息本地事務處理成功,則重新發送 confirm 消息,否則直接回滾該消息

該方案與本地消息最大的不同是去掉了本地消息表,其次本地消息表依賴消息表重試寫入 mq 這一步由本方案中的輪詢 prepare 消息狀態來重試或者回滾該消息替代。其實現條件與餘容錯方案基本一致。目前市面上實現該方案的只有阿里的 RocketMq。

5.5 盡最大努力通知

最大努力通知是最簡單的一種柔性事務,適用於一些最終一致性時間敏感度低的業務,且被動方處理結果 不影響主動方的處理結果。

這個方案的大致意思就是:

  1. 系統 A 本地事務執行完之後,發送個消息到 MQ;

  2. 這裏會有個專門消費 MQ 的服務,這個服務會消費 MQ 並調用系統 B 的接口;

  3. 要是系統 B 執行成功就 ok 了;要是系統 B 執行失敗了,那麼最大努力通知服務就定時嘗試重新調用系統 B, 反覆 N 次,最後還是不行就放棄。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/Cr9Y5yzK0jp3NYVi_UgTLQ