Seata-AT 如何保證分佈式事務一致性

頭圖.png
做者 | 陳健斌(funkye) github id: a364176773
來源|阿里巴巴雲原生公衆號git

Seata 是一款開源的分佈式事務解決方案,star 高達 18100+,社區活躍度極高,致力於在微服務架構下提供高性能和簡單易用的分佈式事務服務,本文將剖析 Seata-AT 的實現原理,讓用戶對 AT 模式有更深刻的認識。github

Seata 事務模式是什麼?

1. Seata 對事務的定義

Seata 定義了全局事務的框架。redis

全局事務定義爲若干分支事務的總體協調:sql

  1. TM 向 TC 請求發起(Begin)、提交(Commit)、回滾(Rollback)全局事務。
  2. TM 把表明全局事務的 XID 綁定到分支事務上。
  3. RM 向 TC 註冊,把分支事務關聯到 XID 表明的全局事務中。
  4. RM 把分支事務的執行結果上報給 TC。(可選)
  5. TC 發送分支提交(Branch Commit)或分支回滾(Branch Rollback)命令給 RM。

1.png

Seata 的全局事務處理過程,分爲兩個階段:mongodb

執行階段>  :執行分支事務,並保證執行結果知足是可回滾的(Rollbackable)和持久化的(Durable)。
完成階段> :根據執行階段結果造成的決議,應用經過 TM 發出的全局提交或回滾的請求給 TC,> TC 命令 RM 驅動 分支事務 進行 Commit 或 Rollback。數據庫

Seata 的所謂事務模式是指:運行在 Seata 全局事務框架下的分支事務的行爲模式。> > 準確地講> ,應該叫做> 分支事務模式> 。性能優化

不一樣的事務模式區別在於分支事務使用不一樣的方式達到全局事務兩個階段的目標。> > 即,回答如下兩個問題:架構

執行階段>  :如何執行並保證執行結果知足是可回滾的(Rollbackable)和持久化的(Durable)。
完成階段> :收到 TC 的命令後,作到事務的回滾/提交。併發

2. 其它二階段事務如何在 Seata 事務框架下運轉

1)TCC 事務模式

首先來看下 TCC 事務如何融合在 Seata 事務框架中:框架

2.png

能夠發現,其實跟 Seata 的事務框架圖長得很是像,而區別爲 RM 負責管理就是一階段的 try 執行和二階段的 confirm/cancel,同樣是由 TM 進行事務的 Begin(發起),RM 被 TM 調用後執行一階段的 Try 方法,等待調用鏈路走完的時候,TM 向 TC 告知二階段決議,此時 TC 對 RM 驅動二階段執行(下發通知,RM 執行 confirm/cancel)。

2)XA 事務模式

3.png

如圖所示,XA 模式其實就是 Seata 底層利用了 XA 接口,在一階段二階段時自動處理。如一階段時,XA 的 RM 經過代理用戶數據源,建立 XAConnection,進行開啓 XA 事務(XA start)和 XA-prepare(此時 XA 的任何操做都會被持久化,即使宕機也能恢復),在二階段時,TC 通知 RM 進行 XA 分支的 Commit/Rollback 操做。

AT 模式是什麼?

首先來看一個例子。

1. 一階段

業務 sql:update product set name = 'GTS' where name = 'TXC'。

一階段的執行過程對用戶是無感知的,用戶側的業務 sql 保持不變,而 AT 模式下一階段具體發生了什麼?接下來,簡單說下。

  • 解析 sql 並查詢獲得前鏡像:select id, name, since from product where name = 'TXC'。
  • 執行業務 sql。
  • 查詢執行後的數據做爲後鏡像:select id, name, since from product where id = 1。

2. 二階段

提交:僅需把事務相關信息刪除便可(理論上不刪除也沒問題)。

回滾:取出前鏡像進行回滾。

經過上述簡單的例子,其實能夠發現,AT 模式就是自動補償式事務,那 AT 具體都作了哪些呢?下文將會講述。

AT 如何保證分佈式事務一致性?

先來看這個圖:

4.png

可能不少人剛看到上圖會有疑問,其實這個就是無侵入式 AT 模式的作法示意圖。首先用戶仍是從接口進入,到達事務發起方,此時對業務開發者來講,這個發起方入口就是一個業務接口罷了,同樣地執行業務 sql,同樣地 return 響應信息給客戶端並無什麼改變。而背後就是用戶的 sql 被 Seata 代理所託管,Seata-AT 模式能感知到用戶的全部 sql,並對之進行操做,來保證一致性。

Seata-AT 是怎麼作到無侵入的呢?

5.jpeg

如圖所示,應用啓動時 Seata 會自動把用戶的 DataSource 代理,對 JDBC 操做熟悉的用戶其實對 DataSource 仍是比較熟悉的,拿到了 DataSource,就等於掌握了數據源鏈接,也就能在背後作些「小動做」,此時對用戶來說也是無感知無***。

以後業務有請求進來,執行業務 sql 時,Seata 會解析用戶的 sql,提取出表元數據,生成前鏡像,再經過執行業務 sql,保存執行 sql 後的後鏡像(至於後鏡像的介紹以後會講到),生成行鎖以後在註冊分支時攜帶到 Seata-Server,也就是 TC 端。

到此爲止,在 Client 端的一階段操做就已經完成了,無感知、無***。此時若是思考下,會發現這裏其實有一個行鎖,這個行鎖是幹什麼用的呢?這就是要接着講到 Seata-AT 是如何保證分佈式下的事務隔離性,這裏直接拿官網的示例來講。

1. 寫隔離

  • 一階段本地事務提交前,須要確保先拿到全局鎖
  • 拿不到全局鎖,不能提交本地事務。
  • 全局鎖的嘗試被限制在必定範圍內,超出範圍將放棄,並回滾本地事務,釋放本地鎖。

以一個示例來講明:

兩個全局事務 tx1 和 tx2,分別對 a 表的 m 字段進行更新操做,m 的初始值 1000。

tx1 先開始,開啓本地事務,拿到本地鎖,更新操做 m = 1000 - 100 = 900。本地事務提交前,先拿到該記錄的全局鎖,本地提交釋放本地鎖。tx2 後開始,開啓本地事務,拿到本地鎖,更新操做 m = 900 - 100 = 800。本地事務提交前,嘗試拿該記錄的全局鎖,tx1 全局提交前,該記錄的全局鎖被 tx1 持有,tx2 須要重試等待全局鎖 

6.png

tx1 二階段全局提交,釋放全局鎖 。tx2 拿到全局鎖提交本地事務。

7.png

若是 tx1 的二階段全局回滾,則 tx1 須要從新獲取該數據的本地鎖,進行反向補償的更新操做,實現分支的回滾。

此時若是 tx2 仍在等待該數據的全局鎖,同時持有本地鎖,則 tx1 的分支回滾會失敗。分支的回滾會一直重試,直到 tx2 的全局鎖等鎖超時,放棄全局鎖並回滾本地事務釋放本地鎖,tx1 的分支回滾最終成功。

由於整個過程全局鎖在 tx1 結束前一直是被 tx1 持有的,因此不會發生髒寫的問題。

這個時候隔離性想必你們已經比較明白了,此時一階段的大部分操做相信你們也比較明白了,接下來咱們繼續往下一階段解析。

2. AT 模式二階段處理

8.jpeg

由上圖可見,在二階段提交時,TC 僅是下發一個通知 :把以前一階段作記錄的 undoLog 刪除,並把相關事務信息如:行鎖刪除,以後讓由於在競爭鎖被阻塞的事務順利進行。

而二階段是回滾時,則要多作一些處理。

9.jpeg

首先在 Client 端收到 TC 告知的二階段是回滾時,會去查到對應的事務的 undolog,取出後鏡像,對比當前的數據(由於 SeataAT 是從業務應用層面進行保護分佈式事務,若是此時在數據庫層面直接修改了庫內信息,這個時候 SeataAT 的行鎖不起隔離性做用),若是出現了在全局事務之外的數據修改,此時斷定爲髒寫,而 Seata 由於沒法感知這個髒寫如何發生,此時只能打印日誌和觸發異常通知,告知用戶須要人工介入(規範修改數據入口可避免髒寫)。

而若是沒有發生髒寫就比較簡單了,拿出前鏡像,衆所皆知事務是須要有原子性的,要麼一塊兒發生,要麼都不發生,此時前鏡像記錄了發生以前的數據,進行回滾後,就達到了相似本地事務那樣的原子性效果。回滾後,再把事務相關信息,如 undolog,行鎖進行刪除。二階段回滾算是告一段落了。

既然介紹完了 AT 模式的一階段及二階段的原理思想方式,那麼 AT 在 Seata 的分佈式事務框架下是怎麼樣的呢?

10.png

能夠看到,AT 與其它事務模式在 Seata 事務框架中,會多出一個 undolog 的表(相對其它模式的***點),可是除此以外,對業務來講,幾乎是零***性,這也就是爲何 AT 模式在 Seata 中受衆普遍的緣由。

3. AT 模式與 Seata 支持的其它二階段模式區別

首先應該明白,目前爲止,不存在有任何一種分佈式事務的能夠知足全部場景。

不管 AT 模式、TCC 模式仍是 Saga 模式,這些模式的提出,本質上都源自 XA 規範對某些場景需求的沒法知足。

目前分爲 3 點來作出對比:

  • 數據鎖定

AT 模式使用全局鎖保障基本的寫隔離,實際上也是鎖定數據的,只不過鎖在 TC 側集中管理,解鎖效率高且沒有阻塞的問題。

TCC 模式無鎖,利用本地事務排他鎖特性,可預留資源,在全局事務決議後執行相應操做。

XA 模式在整個事務處理過程結束前,涉及數據都被鎖定,讀寫都按隔離級別的定義約束起來。

  • 死鎖(協議阻塞)

XA 模式 prepare 後(老版本的數據庫中,須要 XA END 後,再下發 prepare <三階段由來>),分支事務進入阻塞階段,收到 XA commit 或 XA rollback 前必須阻塞等待。

AT 可支持降級,由於鎖存儲在 TC 側,若是 Seata 出現 bug 或者其它問題,可直接降級,對後續業務調用鏈無任何影響。

TCC 無此問題。

  • 性能

性能的損耗主要來自兩個方面:一方面,事務相關處理和協調過程,增長單個事務的 RT;另外一方面,併發事務數據的鎖衝突,下降吞吐。其實主要緣由就是上面的協議阻塞跟數據鎖定形成。

XA 模式它的一階段不提交,在大併發場景因爲鎖存儲在多個資源方(數據庫等),加重了性能耗損。

AT 模式鎖粒度細至行級(須要主鍵),且全部事務鎖存儲在 TC 側,解鎖高效迅速。

TCC 模式性能最優,僅需些許 RPC 開銷,及 2 次本地事務的性能開銷,可是須要符合資源預留場景,且是對業務侵入性較大(須要業務開發者每一個接口分爲 3 個,一個 try,2 個二階段使用的 confirm 和 cancel )。
可能不少同窗對 XA 和 AT 的鎖 & 協議阻塞不是特別理解,那麼直接來看下圖:

11.png

能夠試着猜一下是哪一個是 XA?其實下圖的是 XA,由於它帶來的鎖粒度更大,且鎖定時間更久,致使了併發性能相對 AT 事務模型來講,差的比較多,因此至今XA模式的普及度都不很過高。

Seata 近期規劃

  • 控制檯

首先控制檯是 Seata 用戶暴露已久的一個問題,沒有一個可視化界面,使得用戶對 Seata 的可靠性出現了懷疑,更因爲沒有控制檯,侷限了不少在 Seata 上可人工介入分佈式事務的可能性等問題,因此將來在 1.5.0 的版本會帶來控制檯的加入,也歡迎更多的同窗加入進來一塊兒共建!

  • Raft 集成

Raft 集成的緣由,可能大部分用戶不是特別知曉,首先要知道目前 TC 端的事務信息都是存儲在外部存儲器,好比數據庫、redis、mongodb(PR 階段),這就形成了若是外部存儲宕機,Seata-Server 集羣的徹底不可用。即使 Server 是集羣部署,有 10 個甚至更多節點,都會所以而不可用,這是不可接受的。

因此引入 Raft 來讓每一個 Seata-Server 的事務信息達到一致,即使某個節點宕機,也不會破壞事務信息準確性,從而也讓分佈式事務的一致性獲得了更好的保證。(關於 Seata-Server raft 的實現以後會以新篇章來分享。)

  • undoLog 壓縮

這個是 1.5.0 AT 模式比較大的性能優化,因爲一階段操做的數據多且大,由於 Seata 在背後爲用戶插入了 undolog 信息,由此可能也會變得大,有形成了入庫緩慢的可能,因此要把 undolog 進行壓縮,使 undolog 的插入再也不成爲 AT 事務在分支數據量大的時候成爲一個大的心梗開銷。

總結

AT 說到底就是實現對資源操做的代理,並記錄原先 & 變動後的狀態,並用鎖保證該數據的隔離性。在調用鏈中出現異常時,還原全部分支數據,達到分佈式事務下的「原子性」。

將來呢?redis,mongodb,mq? 盡情期待。

Seata 項目的最核心的價值在於:構建一個全面解決分佈式事務問題的標準化平臺。

基於 Seata,上層應用架構能夠根據實際場景的需求,靈活選擇合適的分佈式事務解決方案,很是歡迎你們參與到項目的建設中,共同打造一個標準化的分佈式事務平臺。

12.png

相關文章
相關標籤/搜索