分散式事務 Seata Saga 模式首秀以及三種模式詳解 | Meetup#3 回顧

螞蟻金服分散式架構發表於2019-08-15

作者:屹遠(陳龍),螞蟻金服分散式事務核心研發 。 本文根據 8月11日 SOFA Meetup#3 廣州站 《分散式事務 Seata 及其三種模式詳解》主題分享整理,著重分享分散式事務產生的背景、理論基礎,以及 Seata 分散式事務的原理以及三種模式(AT、TCC、Saga)的分散式事務實現。

本次分享的視訊回顧以及 PPT 檢視地址:tech.antfin.com/community/a…

分散式事務 Seata 三種模式詳解

一、分散式事務產生的背景

1.1 分散式架構演進之 - 資料庫的水平拆分

螞蟻金服的業務資料庫起初是單庫單表,但隨著業務資料規模的快速發展,資料量越來越大,單庫單表逐漸成為瓶頸。所以我們對資料庫進行了水平拆分,將原單庫單表拆分成資料庫分片。

如下圖所示,分庫分表之後,原來在一個資料庫上就能完成的寫操作,可能就會跨多個資料庫,這就產生了跨資料庫事務問題。

資料庫的水平拆分

1.2 分散式架構演進之 - 業務服務化拆分

在業務發展初期,“一塊大餅”的單業務系統架構,能滿足基本的業務需求。但是隨著業務的快速發展,系統的訪問量和業務複雜程度都在快速增長,單系統架構逐漸成為業務發展瓶頸,解決業務系統的高耦合、可伸縮問題的需求越來越強烈。

如下圖所示,螞蟻金服按照面向服務架構(SOA)的設計原則,將單業務系統拆分成多個業務系統,降低了各系統之間的耦合度,使不同的業務系統專注於自身業務,更有利於業務的發展和系統容量的伸縮。

業務服務化拆分

業務系統按照服務拆分之後,一個完整的業務往往需要呼叫多個服務,如何保證多個服務間的資料一致性成為一個難題。

二、分散式事務理論基礎

2.1 兩階段提交協議

兩階段提交協議

兩階段提交協議:事務管理器分兩個階段來協調資源管理器,第一階段準備資源,也就是預留事務所需的資源,如果每個資源管理器都資源預留成功,則進行第二階段資源提交,否則協調資源管理器回滾資源。

2.2 TCC

TCC

TCC(Try-Confirm-Cancel) 實際上是服務化的兩階段提交協議,業務開發者需要實現這三個服務介面,第一階段服務由業務程式碼編排來呼叫 Try 介面進行資源預留,所有參與者的 Try 介面都成功了,事務管理器會提交事務,並呼叫每個參與者的 Confirm 介面真正提交業務操作,否則呼叫每個參與者的 Cancel 介面回滾事務。

2.3 Saga

Saga

Saga 是一種補償協議,在 Saga 模式下,分散式事務內有多個參與者,每一個參與者都是一個衝正補償服務,需要使用者根據業務場景實現其正向操作和逆向回滾操作。

分散式事務執行過程中,依次執行各參與者的正向操作,如果所有正向操作均執行成功,那麼分散式事務提交。如果任何一個正向操作執行失敗,那麼分散式事務會退回去執行前面各參與者的逆向回滾操作,回滾已提交的參與者,使分散式事務回到初始狀態。

Saga 理論出自 Hector & Kenneth 1987發表的論文 Sagas。

Saga 正向服務與補償服務也需要業務開發者實現。

三、Seata 及其三種模式詳解

3.1 分散式事務 Seata 介紹

Seata(Simple Extensible Autonomous Transaction Architecture,簡單可擴充套件自治事務框架)是 2019 年 1 月份螞蟻金服和阿里巴巴共同開源的分散式事務解決方案。Seata 開源半年左右,目前已經有超過 1.1 萬 star,社群非常活躍。我們熱忱歡迎大家參與到 Seata 社群建設中,一同將 Seata 打造成開源分散式事務標杆產品。

Seata:github.com/seata/seata

Seata

3.2 分散式事務 Seata 產品模組

如下圖所示,Seata 中有三大模組,分別是 TM、RM 和 TC。 其中 TM 和 RM 是作為 Seata 的客戶端與業務系統整合在一起,TC 作為 Seata 的服務端獨立部署。

Seata 三大模組

在 Seata 中,分散式事務的執行流程:

  • TM 開啟分散式事務(TM 向 TC 註冊全域性事務記錄);
  • 按業務場景,編排資料庫、服務等事務內資源(RM 向 TC 彙報資源準備狀態);
  • TM 結束分散式事務,事務一階段結束(TM 通知 TC 提交/回滾分散式事務);
  • TC 彙總事務資訊,決定分散式事務是提交還是回滾;
  • TC 通知所有 RM 提交/回滾 資源,事務二階段結束;

3.3 分散式事務 Seata 解決方案

Seata 會有 4 種分散式事務解決方案,分別是 AT 模式、TCC 模式、Saga 模式和 XA 模式。

Seata 的 4 種分散式事務解決方案

3.3.1 AT 模式

今年 1 月份,Seata 開源了 AT 模式。AT 模式是一種無侵入的分散式事務解決方案。在 AT 模式下,使用者只需關注自己的“業務 SQL”,使用者的 “業務 SQL” 作為一階段,Seata 框架會自動生成事務的二階段提交和回滾操作。

AT 模式

AT 模式如何做到對業務的無侵入 :
  • 一階段:

在一階段,Seata 會攔截“業務 SQL”,首先解析 SQL 語義,找到“業務 SQL”要更新的業務資料,在業務資料被更新前,將其儲存成“before image”,然後執行“業務 SQL”更新業務資料,在業務資料更新之後,再將其儲存成“after image”,最後生成行鎖。以上操作全部在一個資料庫事務內完成,這樣保證了一階段操作的原子性。

AT 模式一階段

  • 二階段提交:

二階段如果是提交的話,因為“業務 SQL”在一階段已經提交至資料庫, 所以 Seata 框架只需將一階段儲存的快照資料和行鎖刪掉,完成資料清理即可。

AT 模式二階段提交

  • 二階段回滾:

二階段如果是回滾的話,Seata 就需要回滾一階段已經執行的“業務 SQL”,還原業務資料。回滾方式便是用“before image”還原業務資料;但在還原前要首先要校驗髒寫,對比“資料庫當前業務資料”和 “after image”,如果兩份資料完全一致就說明沒有髒寫,可以還原業務資料,如果不一致就說明有髒寫,出現髒寫就需要轉人工處理。

AT 模式二階段回滾

AT 模式的一階段、二階段提交和回滾均由 Seata 框架自動生成,使用者只需編寫“業務 SQL”,便能輕鬆接入分散式事務,AT 模式是一種對業務無任何侵入的分散式事務解決方案。

3.3.2 TCC 模式

2019 年 3 月份,Seata 開源了 TCC 模式,該模式由螞蟻金服貢獻。TCC 模式需要使用者根據自己的業務場景實現 Try、Confirm 和 Cancel 三個操作;事務發起方在一階段執行 Try 方式,在二階段提交執行 Confirm 方法,二階段回滾執行 Cancel 方法。

TCC 模式

TCC 三個方法描述:

  • Try:資源的檢測和預留;
  • Confirm:執行的業務操作提交;要求 Try 成功 Confirm 一定要能成功;
  • Cancel:預留資源釋放;

螞蟻金服在 TCC 的實踐經驗

螞蟻金服在 TCC 的實踐經驗

1 TCC 設計 - 業務模型分 2 階段設計:

使用者接入 TCC ,最重要的是考慮如何將自己的業務模型拆成兩階段來實現。

以“扣錢”場景為例,在接入 TCC 前,對 A 賬戶的扣錢,只需一條更新賬戶餘額的 SQL 便能完成;但是在接入 TCC 之後,使用者就需要考慮如何將原來一步就能完成的扣錢操作,拆成兩階段,實現成三個方法,並且保證一階段 Try  成功的話 二階段 Confirm 一定能成功。

TCC 設計 - 業務模型分 2 階段設計

如上圖所示,Try 方法作為一階段準備方法,需要做資源的檢查和預留。在扣錢場景下,Try 要做的事情是就是檢查賬戶餘額是否充足,預留轉賬資金,預留的方式就是凍結 A 賬戶的 轉賬資金。Try 方法執行之後,賬號 A 餘額雖然還是 100,但是其中 30 元已經被凍結了,不能被其他事務使用。

二階段 Confirm 方法執行真正的扣錢操作。Confirm 會使用 Try 階段凍結的資金,執行賬號扣款。Confirm 方法執行之後,賬號 A 在一階段中凍結的 30 元已經被扣除,賬號 A 餘額變成 70 元 。

如果二階段是回滾的話,就需要在 Cancel 方法內釋放一階段 Try 凍結的 30 元,使賬號 A 的回到初始狀態,100 元全部可用。

使用者接入 TCC 模式,最重要的事情就是考慮如何將業務模型拆成 2 階段,實現成 TCC 的 3 個方法,並且保證 Try 成功 Confirm 一定能成功。相對於 AT 模式,TCC 模式對業務程式碼有一定的侵入性,但是 TCC 模式無 AT 模式的全域性行鎖,TCC 效能會比 AT 模式高很多。

2 TCC 設計 - 允許空回滾:

TCC 設計 - 允許空回滾

Cancel 介面設計時需要允許空回滾。在 Try 介面因為丟包時沒有收到,事務管理器會觸發回滾,這時會觸發 Cancel 介面,這時 Cancel 執行時發現沒有對應的事務 xid 或主鍵時,需要返回回滾成功。讓事務服務管理器認為已回滾,否則會不斷重試,而 Cancel 又沒有對應的業務資料可以進行回滾。

3 TCC 設計 - 防懸掛控制:

TCC 設計 - 防懸掛控制

懸掛的意思是:Cancel 比 Try 介面先執行,出現的原因是 Try 由於網路擁堵而超時,事務管理器生成回滾,觸發 Cancel 介面,而最終又收到了 Try 介面呼叫,但是 Cancel 比 Try 先到。按照前面允許空回滾的邏輯,回滾會返回成功,事務管理器認為事務已回滾成功,則此時的 Try 介面不應該執行,否則會產生資料不一致,所以我們在 Cancel 空回滾返回成功之前先記錄該條事務 xid 或業務主鍵,標識這條記錄已經回滾過,Try 介面先檢查這條事務xid或業務主鍵如果已經標記為回滾成功過,則不執行 Try 的業務操作。

4 TCC 設計 - 冪等控制:

CC 設計 - 冪等控制

冪等性的意思是:對同一個系統,使用同樣的條件,一次請求和重複的多次請求對系統資源的影響是一致的。因為網路抖動或擁堵可能會超時,事務管理器會對資源進行重試操作,所以很可能一個業務操作會被重複呼叫,為了不因為重複呼叫而多次佔用資源,需要對服務設計時進行冪等控制,通常我們可以用事務 xid 或業務主鍵判重來控制。

3.3.3 Saga 模式

Saga 模式是 Seata 即將開源的長事務解決方案,將由螞蟻金服主要貢獻。在 Saga 模式下,分散式事務內有多個參與者,每一個參與者都是一個衝正補償服務,需要使用者根據業務場景實現其正向操作和逆向回滾操作。

分散式事務執行過程中,依次執行各參與者的正向操作,如果所有正向操作均執行成功,那麼分散式事務提交。如果任何一個正向操作執行失敗,那麼分散式事務會去退回去執行前面各參與者的逆向回滾操作,回滾已提交的參與者,使分散式事務回到初始狀態。

Saga 模式

Saga 模式下分散式事務通常是由事件驅動的,各個參與者之間是非同步執行的,Saga 模式是一種長事務解決方案。

1 Saga 模式使用場景

Saga 模式使用場景

Saga 模式適用於業務流程長且需要保證事務最終一致性的業務系統,Saga 模式一階段就會提交本地事務,無鎖、長流程情況下可以保證效能。

事務參與者可能是其它公司的服務或者是遺留系統的服務,無法進行改造和提供 TCC 要求的介面,可以使用 Saga 模式。

Saga模式的優勢是:

  • 一階段提交本地資料庫事務,無鎖,高效能;
  • 參與者可以採用事務驅動非同步執行,高吞吐;
  • 補償服務即正向服務的“反向”,易於理解,易於實現;

缺點:Saga 模式由於一階段已經提交本地資料庫事務,且沒有進行“預留”動作,所以不能保證隔離性。後續會講到對於缺乏隔離性的應對措施。

2 基於狀態機引擎的 Saga 實現

基於狀態機引擎的 Saga 實現

目前 Saga 的實現一般也兩種,一種是通過事件驅動架構實現,一種是基於註解加攔截器攔截業務的正向服務實現。Seata 目前是採用事件驅動的機制來實現的,Seata 實現了一個狀態機,可以編排服務的呼叫流程及正向服務的補償服務,生成一個 json 檔案定義的狀態圖,狀態機引擎驅動到這個圖的執行,當發生異常的時候狀態機觸發回滾,逐個執行補償服務。當然在什麼情況下觸發回滾使用者是可以自定義決定的。該狀態機可以實現服務編排的需求,它支援單項選擇、併發、非同步、子狀態機呼叫、引數轉換、引數對映、服務執行狀態判斷、異常捕獲等功能。

3 狀態機引擎原理

狀態機引擎原理

該狀態機引擎的基本原理是,它基於事件驅動架構,每個步驟都是非同步執行的,步驟與步驟之間通過事件佇列流轉, 極大的提高系統吞吐量。每個步驟執行時會記錄事務日誌,用於出現異常時回滾時使用,事務日誌會記錄在與業務表所在的資料庫內,提高效能。

4 狀態機引擎設計

狀態機引擎設計

該狀態機引擎分成了三層架構的設計,最底層是“事件驅動”層,實現了 EventBus 和消費事件的執行緒池,是一個 Pub-Sub 的架構。第二層是“流程控制器”層,它實現了一個極簡的流程引擎框架,它驅動一個“空”的流程執行,“空”的意思是指它不關心流程節點做什麼事情,它只執行每個節點的 process 方法,然後執行 route 方法流轉到下一個節點。這是一個通用框架,基於這兩層,開發者可以實現任何流程引擎。最上層是“狀態機引擎”層,它實現了每種狀態節點的“行為”及“路由”邏輯程式碼,提供 API 和狀態圖倉庫,同時還有一些其它元件,比如表示式語言、邏輯計算器、流水生成器、攔截器、配置管理、事務日誌記錄等。

5 Saga 服務設計經驗

和TCC類似,Saga的正向服務與反向服務也需求遵循以下設計原則:

1)Saga 服務設計 - 允許空補償

Saga 服務設計 - 允許空補償

2)Saga 服務設計 - 防懸掛控制

Saga 服務設計 - 防懸掛控制

3)Saga 服務設計 - 冪等控制

Saga 服務設計 - 冪等控制

4)Saga 設計 - 自定義事務恢復策略

Saga 設計 - 自定義事務恢復策略

前面講到 Saga 模式不保證事務的隔離性,在極端情況下可能出現髒寫。比如在分散式事務未提交的情況下,前一個服務的資料被修改了,而後面的服務發生了異常需要進行回滾,可能由於前面服務的資料被修改後無法進行補償操作。這時的一種處理辦法可以是“重試”繼續往前完成這個分散式事務。由於整個業務流程是由狀態機編排的,即使是事後恢復也可以繼續往前重試。所以使用者可以根據業務特點配置該流程的事務處理策略是優先“回滾”還是“重試”,當事務超時的時候,Server 端會根據這個策略不斷進行重試。

由於 Saga 不保證隔離性,所以我們在業務設計的時候需要做到“寧可長款,不可短款”的原則,長款是指在出現差錯的時候站在我方的角度錢多了的情況,錢少了則是短款,因為如果長款可以給客戶退款,而短款則可能錢追不回來了,也就是說在業務設計的時候,一定是先扣客戶帳再入帳,如果因為隔離性問題造成覆蓋更新,也不會出現錢少了的情況。

6 基於註解和攔截器的 Saga 實現

基於註解和攔截器的 Saga 實現

還有一種 Saga 的實現是基於註解+攔截器的實現,Seata 目前沒有實現,可以看上面的虛擬碼來理解一下,one 方法上定義了 @SagaCompensable 的註解,用於定義 one 方法的補償方法是 compensateOne 方法。然後在業務流程程式碼 processA 方法上定義 @SagaTransactional 註解,啟動 Saga 分散式事務,通過攔截器攔截每個正向方法當出現異常的時候觸發回滾操作,呼叫正向方法的補償方法。

7 兩種 Saga 實現優劣對比

兩種 Saga 的實現各有又缺點,下面表格是一個對比:

兩種 Saga 實現優劣對比

狀態機引擎的最大優勢是可以通過事件驅動的方法非同步執行提高系統吞吐,可以實現服務編排需求,在 Saga 模式缺乏隔離性的情況下,可以多一種“向前重試”的事情恢復策略。註解加攔截器的的最大優勢是,開發簡單、學習成本低。

總結

本文先回顧了分散式事務產生的背景及理論基礎,然後重點講解了 Seata 分散式事務的原理以及三種模式(AT、TCC、Saga)的分散式事務實現。

Seata 的定位是分散式事全場景解決方案,未來還會有 XA 模式的分散式事務實現,每種模式都有它的適用場景,AT 模式是無侵入的分散式事務解決方案,適用於不希望對業務進行改造的場景,幾乎0學習成本。TCC 模式是高效能分散式事務解決方案,適用於核心系統等對效能有很高要求的場景。Saga 模式是長事務解決方案,適用於業務流程長且需要保證事務最終一致性的業務系統,Saga 模式一階段就會提交本地事務,無鎖,長流程情況下可以保證效能,多用於渠道層、整合層業務系統。事務參與者可能是其它公司的服務或者是遺留系統的服務,無法進行改造和提供 TCC 要求的介面,也可以使用 Saga 模式。

公眾號:金融級分散式架構(Antfin_SOFA)

相關文章