Seata 分布式事務 XA 與 AT 全面解析
Seata 是一款開源的分布式事務解決方案,star 高達 19200+,社區(qū)活躍度極高,致力于在微服務架構(gòu)下提供高性能和簡單易用的分布式事務服務。
目錄
- XA模式是什么?
- 什么是 Seata 的事務模式?
- AT模式是什么?
- 為什么Seata要支持XA模式?
- AT與XA之間的關(guān)系
- 總結(jié)
1. XA模式是什么?
首先正如煊檍兄所言,了解了什么是XA與什么是Seata定義的事務模式,便一目了然。
1.1 什么是XA
用非常官方的話來說
XA 規(guī)范 是 X/Open 組織定義的分布式事務處理(DTP,Distributed Transaction Processing)標準。
XA 規(guī)范 描述了全局的事務管理器與局部的資源管理器之間的接口。XA規(guī)范 的目的是允許的多個資源(如數(shù)據(jù)庫,應用服務器,消息隊列等)在同一事務中訪問,這樣可以使 ACID 屬性跨越應用程序而保持有效。
XA 規(guī)范 使用兩階段提交(2PC,Two-Phase Commit)來保證所有資源同時提交或回滾任何特定的事務。
XA 規(guī)范 在上世紀 90 年代初就被提出。目前,幾乎所有主流的數(shù)據(jù)庫都對 XA 規(guī)范 提供了支持。
1.2 什么是Seata的事務模式?
Seata 定義了全局事務的框架。全局事務 定義為若干 分支事務 的整體協(xié)調(diào):1.TM 向 TC 請求發(fā)起(Begin)、提交(Commit)、回滾(Rollback)全局事務。2.TM 把代表全局事務的 XID 綁定到分支事務上。3.RM 向 TC 注冊,把分支事務關(guān)聯(lián)到 XID 代表的全局事務中。4.RM 把分支事務的執(zhí)行結(jié)果上報給 TC。(可選) 5.TC 發(fā)送分支提交(Branch Commit)或分支回滾(Branch Rollback)命令給 RM。
Seata 的 全局事務 處理過程,分為兩個階段:執(zhí)行階段 :執(zhí)行 分支事務,并 保證 執(zhí)行結(jié)果滿足是 可回滾的(Rollbackable) 和 持久化的(Durable)。完成階段:根據(jù) 執(zhí)行階段 結(jié)果形成的決議,應用通過 TM 發(fā)出的全局提交或回滾的請求給 TC, TC 命令 RM 驅(qū)動 分支事務 進行 Commit 或 Rollback。Seata 的所謂 事務模式 是指:運行在 Seata 全局事務框架下的 分支事務 的行為模式。準確地講,應該叫作 分支事務模式。不同的 事務模式 區(qū)別在于 分支事務 使用不同的方式達到全局事務兩個階段的目標。即,回答以下兩個問題:執(zhí)行階段 :如何執(zhí)行并 保證 執(zhí)行結(jié)果滿足是 可回滾的(Rollbackable) 和 持久化的(Durable)。完成階段:收到 TC 的命令后,做到事務的回滾/提交
2. 那么什么是Seata XA 模式?
XA 模式:在 Seata 定義的分布式事務框架內(nèi),利用事務資源(數(shù)據(jù)庫、消息服務等)對 XA 協(xié)議的支持,以 XA 協(xié)議的機制來管理分支事務的一種 事務模式。執(zhí)行階段:可回滾:業(yè)務 SQL 操作放在 XA 分支中進行,由資源對 XA 協(xié)議的支持來保證 可回滾 持久化:XA 分支完成后,執(zhí)行 XA prepare,同樣,由資源對 XA 協(xié)議的支持來保證 持久化(即,之后任何意外都不會造成無法回滾的情況) 完成階段:分支提交:執(zhí)行 XA 分支的 commit 分支回滾:執(zhí)行 XA 分支的 rollback
以下是XA模式在Seata所定義的事務模式下的設計模型圖片
2.1 什么是Seata AT(TXC) 模式?
去年 1 月份,Seata 開源了 AT 模式。AT 模式是一種無侵入的分布式事務解決方案。在 AT 模式下,用戶只需關(guān)注自己的“業(yè)務 SQL”,用戶的 “業(yè)務 SQL” 作為一階段,Seata 框架會自動生成事務的二階段提交和回滾操作。
通過簡介,其實可以發(fā)現(xiàn)AT模式的特點,只需關(guān)注自己的業(yè)務sql,對業(yè)務無入侵的一種分布式事務模式。那么我們應該知道他是怎么對業(yè)務做到無入侵的?
2.2 AT 模式如何做到對業(yè)務的無侵入 ?
AT模式一階段
- 首先,在Seata的組件中,如果你想開啟分布式事務,那么就應該在你的業(yè)務入口或者事務發(fā)起入口加上@GlobalTransactional注解
- 如果你是AT模式就要做好數(shù)據(jù)源代理(seata1.0后全面支持自動代理),并被sqlsessionfactroy使用(或者直接jdbc操作使用被代理數(shù)據(jù)源)
可以發(fā)現(xiàn)比較關(guān)鍵的異步,與其他模式的區(qū)別便是代理數(shù)據(jù)源,而代理數(shù)據(jù)源又有什么奧秘呢?
如上圖所示,你的數(shù)據(jù)源被代理后,通過被DataSourceProxy代理后,你所執(zhí)行的sql,會被提取,解析,保存前鏡像后,再執(zhí)行業(yè)務sql,再保存后鏡像,以便與后續(xù)出現(xiàn)異常,進行二階段的回滾操作。
2.3 AT 模式如何保證隔離性
首先我們拿到官網(wǎng)所展示的文檔來更直觀的描述:
可以通過上圖得出:
一階段本地事務提交前,需要確保先拿到 全局鎖 。
拿不到 全局鎖 ,不能提交本地事務。
拿 全局鎖 的嘗試被限制在一定范圍內(nèi),超出范圍將放棄,并回滾本地事務,釋放本地鎖。
兩個全局事務 tx1 和 tx2,分別對 a 表的 m 字段進行更新操作,m 的初始值 1000。
tx1 先開始,開啟本地事務,拿到本地鎖,更新操作 m = 1000 - 100 = 900。
本地事務提交前,先拿到該記錄的 全局鎖 ,本地提交釋放本地鎖。
tx2 后開始,開啟本地事務,拿到本地鎖,更新操作 m = 900 - 100 = 800。
本地事務提交前,嘗試拿該記錄的 全局鎖 ,tx1 全局提交前,
該記錄的全局鎖被 tx1 持有,tx2 需要重試等待 全局鎖 ,如tx2等待所超時,那么tx2便回滾本地事務所以他不會產(chǎn)生臟數(shù)據(jù)。
AT 模式二階段提交
二階段如果是提交的話,因為“業(yè)務 SQL”在一階段已經(jīng)提交至數(shù)據(jù)庫, 所以 Seata 框架只需將一階段保存的快照數(shù)據(jù)和行鎖刪掉,完成數(shù)據(jù)清理即可。
AT 模式二階段回滾
二階段如果是回滾的話,Seata 就需要回滾一階段已經(jīng)執(zhí)行的“業(yè)務 SQL”,還原業(yè)務數(shù)據(jù)?;貪L方式便是用“before image”還原業(yè)務數(shù)據(jù);但在還原前要首先要校驗臟寫, 對比“數(shù)據(jù)庫當前業(yè)務數(shù)據(jù)”和 “after image”, 如果兩份數(shù)據(jù)完全一致就說明沒有臟寫,可以還原業(yè)務數(shù)據(jù),如果不一致就說明有臟寫, 出現(xiàn)臟寫就需要轉(zhuǎn)人工處理。
完整的AT在Seata所制定的事務模式下的模型圖:
3. 為什么支持XA?
首先我們應該從AT去做判斷,為什么Seata有了AT模式還去做XA的支持
- 從視角出發(fā):首先,我們來總結(jié)下AT模式,首先所有的事物發(fā)起,都是從TM(不僅AT) 且數(shù)據(jù)的讀已提交只能在應用中見效(用戶自行開發(fā)的系統(tǒng)),對資源的查看,無法做到全方面 而XA可讓資源也感知到自身已處于全局事務中,對資源的隔離性可由數(shù)據(jù)庫本身來實現(xiàn),滿足 全局一致性
- 從入侵性,數(shù)據(jù)庫支持角度:業(yè)務無入侵的更徹底,少于2個服務的操作,僅使用本地事務即可滿足一致性,而AT需要 全局鎖來保證隔離性,所以無論是1個服務,單庫的操作,還是n個服務都需要開啟全局事務來保證 隔離性。對數(shù)據(jù)庫的支持,如果AT需要支持mysql,pgsql,oracle以外的數(shù)據(jù)庫,需要做適配,并且 對復雜sql的解析成本更大,開發(fā)效率低,支持的sql數(shù)量少,XA可全方位支持數(shù)據(jù)庫的sql語句 多語言支持,如果你有java應用已經(jīng)使用了seata xa那么本地數(shù)據(jù)庫已經(jīng)幫我們保證了隔離 性,即便其余seata不支持的語言和java并行處理下,數(shù)據(jù)也不會出現(xiàn)不一致的情況。
4. 為什么Seata要支持XA模式?
- 數(shù)據(jù)鎖定:在整個事務處理過程結(jié)束前,涉及數(shù)據(jù)都被鎖定,讀寫都按隔離級別的定義約束起來。
- AT 模式使用 全局鎖 保障基本的 寫隔離,實際上也是鎖定數(shù)據(jù)的,只不過鎖在 TC 側(cè)集中管理 解鎖效率高且沒有阻塞的問題,且XA本地數(shù)據(jù)庫可能持有間隙鎖,造成鎖的粒度更大,鎖定更多無辜數(shù)據(jù)
- 死鎖(協(xié)議阻塞):XA prepare 后,分支事務進入阻塞階段,收到 XA commit 或 XA rollback 前必須阻塞等待。如果沒有一個靠譜的協(xié)調(diào)者存在,比如abc三個庫的數(shù)據(jù)被二階段決議為提交,此時ab收到的指令,提交后,c庫在收到指令后掛了,并沒有提交xa事務,或者協(xié)調(diào)者沒有做到二階段重試,那么這個沒有提交的xa事務將會一直 持有鎖,造成死鎖的局面
- 性能差:性能的損耗主要來自兩個方面:一方面,事務協(xié)調(diào)過程,增加單個事務的 RT;另一方面,并發(fā)事務數(shù) 據(jù)的鎖沖突,降低吞吐。其實主要原因就是上面的阻塞跟數(shù)據(jù)鎖定造成,因為xa的一階段并非提交,如果一階段都是提交的場景下,由于At模式的一階段提交,at的性能是優(yōu)于xa,因為它鎖在tc一側(cè)集中釋放,無需多個庫進行本地的鎖釋放
AT 與 XA 的關(guān)系
首先,我們要明確,無論是AT還是XA,他們都是有利用到數(shù)據(jù)庫自帶的事務特性,來保證數(shù)據(jù)一致性和隔離性
比如AT一階段提交和二階段回滾,都是執(zhí)行了本地事務。比如XA的一階段和二階段,也都是利用了數(shù)據(jù)庫本身的事務特性
那么這樣一樣我們是否應該在數(shù)據(jù)庫層面進行挖掘,AT與XA的關(guān)系呢?
首先這個時候,我們肯定要從中找相同,與找不同。AT首當其沖,他有個必須品,也就是undolog表,undolog,相 信了解數(shù)據(jù)庫的同學肯定是知道。數(shù)據(jù)庫有六種日志分別是:重做日志(redo log)、回滾日志(undo log)、二進制日志(binlog)、錯誤日志(errorlog)、 慢查詢?nèi)罩?slow query log)、一般查詢?nèi)罩?general log),中繼日志(relay log)
那么數(shù)據(jù)庫的undolog是做什么用的呢?undolog保存了事務發(fā)生之前的數(shù)據(jù)的一個版本,可以用于回滾,同時可以提供多版本并發(fā)控制下的讀(MVCC)
可以發(fā)現(xiàn)數(shù)據(jù)庫的undolog跟seata at模式的undolog的作用不謀而合,所以可以判斷,at模式的undolog就是把本地事務作用中的undolog,利用他的原理,做到了分布式事務中,來保證了分布式事務下的事務一致性。
那么說完了undolog,redolog呢?
Redolog的作用便是防止在發(fā)生故障的時間點,尚有臟頁未寫入磁盤,在重啟mysql服務的時候,根據(jù)redo log進行 重做,從而達到事務的持久性這一特性。
那么為什么Seata AT模式?jīng)]有看到redolog的存在?其實很簡單,這個redolog被隱藏的很深,也就是AT模式的一階段提交,讓數(shù)據(jù)庫作為我們的redolog,保證一階段的數(shù)據(jù)準確落盤。
這個時候是不是會想到LCN事務模式?他的undolog由數(shù)據(jù)庫來保證,缺少了一個redolog的存在。其實大可不必思念LCN事務,解析到這里,如果把AT改為一階段不提交,二階段提交時,前鏡像便是undolog,后鏡像便是redolog,也就是說AT其實就是一個不在數(shù)據(jù)庫層面,按照數(shù)據(jù)庫事務思想和實現(xiàn)原理的方式,做到了分布式中的事務一致性。
這時候講到這里,XA跟AT的關(guān)系應該一幕了然了,準確的說,其實應該說是分布式事務跟數(shù)據(jù)庫本地事務的關(guān)系,可以說XA的缺點造成了AT模式的出生,鎖在多側(cè)(多個庫),資源阻塞,性能差。
而AT就像為了把事務的實現(xiàn)決定權(quán)從數(shù)據(jù)庫手中,放到了Seata中,自實現(xiàn)sql解析,自實現(xiàn)undolog(redolog),既然我們沒有 辦法去直接優(yōu)化數(shù)據(jù)庫在分布式事務下的問題,那么不如創(chuàng)造一個新的模式,去其糟粕,取其精華。
Seata AT 與 XA 的優(yōu)劣
其實上面零零碎碎也說了不少各自的優(yōu)缺點,現(xiàn)在我們總結(jié)一下分3點來做比較
- sql支持
- 隔離性
- 侵入性
我們先講第一點,由于上面我們總結(jié)了,其實AT就是一個自實現(xiàn)的XA事務,所以其實可以知道,AT在sql支持上,是遠不及利用本地事務的XA模式,既然AT需要做sql解析,那么背后的實現(xiàn)只能自己來解決,也就是靠Seata社區(qū)的貢獻者們來貢獻解決方案,這是一個長期性的關(guān)鍵問題,但是依然有很多用戶選擇了重寫sql,來獲取AT事務模式的支持。在sql支持上XA無疑是完勝的
第二點隔離性,Seata AT模式通過解析sql獲取涉及的主鍵id,生成行鎖,也就是AT模式的隔離就是靠全局鎖來保證,粒度細至行級,鎖信息存儲在Seata-Server一側(cè)。XA模式的隔離性就是由本地數(shù)據(jù)庫保證,鎖存儲在各個本地數(shù)據(jù)庫中。由于XA模式一旦執(zhí)行了prepare后,再也無法重入這個XA事務,也無法跟其他XA事務共享鎖。因為XA協(xié)議,僅是通過XID來start一個xa事務,本身它不存在所謂的分支事務說法,它本事就是一個XA事務而已,也就是說它只管它自己。這時候可能由同學有疑問了,為什么我在branch_table里看到里XA分支事務呢?其實這個問題根據(jù)上面的什么是Seata事務模式可以了解到,Seata的事務模式就是由全局事務,分支事務,鎖信息所組成。而XA的分支事務,僅僅是作為一個參與方的存在,也就是說這個XA分支在Seata定義中為分支事務,作為分支信息記錄在案,方便宕機后也可以下發(fā)二階段決議信息。而AT由于鎖是自實現(xiàn),也就相對XA來說,我只要知道用戶sql涉及到的數(shù)據(jù),是不是數(shù)據(jù)這個全局事務下的,只要是我默認他就可以使用這個鎖,也就解決了重入問題。我們可以得出總結(jié),XA的隔離性是全局的,AT的隔離性是更靈活且相對全局的(保證所有對數(shù)據(jù)的寫操作被Seata事務覆蓋)。第三點,入侵性,通過我們以上的信息,其實可以發(fā)現(xiàn),誰更底層,誰的入侵性更小,所以由數(shù)據(jù)庫自身所支持的XA模式來說,無疑入侵最小,使用成本最低。
其實說到這里,大家可能會覺得XA模式怎么感覺比AT好這么多,雖然他不支持鎖重入,但是我可以避免這個情況發(fā)生呀。這時候,我畫個圖,大家可能會比較理解
上圖中,右側(cè)圖1是at模式運行時,圖2時xa模式運行時??梢院苊黠@,xa的阻塞帶來的性能下降時非常厲害的,特別是你的分支事務非常多,每個資源的釋放必須等到每個分支的數(shù)據(jù)庫去單獨釋放,后續(xù)的事務才能進入。雖然XA帶來的無侵入非常高,但是由于性能下降的程度太大,也就促使了AT的誕生,而現(xiàn)在AT,TCC,SAGA的模式的接受度也越來越高,這也正說明了開發(fā)者對性能的要求。AT可以看作時由Seata社區(qū)進行全方面優(yōu)化,自研的XA模式,最大特點就是解決了XA模式的性能差的問題。TCC由Seata決定二階段狀態(tài)通知,其使用全權(quán)交托用戶,性能僅僅是2個本地事務+些許rpc開銷。SAGA整個事務鏈路,事務處理全權(quán)交托用戶編排,性能完全由用戶來保證,Seata作為事務的協(xié)助方,記錄全局事務的運行狀態(tài)。可以看出來,越高入侵性的模式其實背后可優(yōu)化的點更多,越少入侵性的,也就是會被局限,只能依托組件開發(fā)者進行不定期的優(yōu)化來保證性能。
總結(jié)
在當前的技術(shù)發(fā)展中,目前分布式事務就是屬于扮演東風的角色,大量的分布式,微服務化,帶來的性能提 升非常明顯,但是卻缺少一個有利的保障,我相信Seata就是承擔著這樣的一個角色,讓萬事俱備不欠東風。Seata項目的最核心的價值在于:
構(gòu)建一個全面解決分布式事務問題的 標準化 平臺。
基于 Seata,上層應用架構(gòu)可以根據(jù)實際場景的需求,靈活選擇合適的分布式事務解決方案。
圖片
注:本期分享借鑒于 Seata 三位 PMC 清銘、煊檍、屹遠
分享人:陳健斌(funkye) github id: a364176773
作者介紹:同盾科技高級開發(fā)工程師 、Seata Committer、Spring cloud alibaba contributor,、Mybatis-Plus contributor(by dynamic-datasource)