一、分布式事務產生得原因:
1.1、數據庫分庫分表
當數據庫單表一年產生的數據超過1000W,那么就要考慮分庫分表,具體分庫分表的原理在此不做解釋,以后有空詳細說,簡單的說就是原來的一個數據庫變成了多個數據庫。這時候,如果一個操作既訪問01庫,又訪問02庫,而且要保證數據的一致性,那么就要用到分布式事務
1.2應用SOA化
所謂的SOA化,就是業務的服務化。比如原來單機支撐了整個電商網站,現在對整個網站進行拆解,分離出了訂單中心、用戶中心、庫存中心。對於訂單中心,有專門的數據庫存儲訂單信息,用戶中心也有專門的數據庫存儲用戶信息,庫存中心也會有專門的數據庫存儲庫存信息。這時候如果要同時對訂單和庫存進行操作,那么就會涉及到訂單數據庫和庫存數據庫,為了保證數據一致性,就需要用到分布式事務。
二、分布式事務解決方案XA模式
XA是一個分布式事務協議,由Tuxedo提出。XA中大致分為兩部分:事務管理器和本地資源管理器。其中本地資源管理器往往由數據庫實現,比如Oracle、DB2這些商業數據庫都實現了XA接口,而事務管理器作為全局的調度者,負責各個本地資源的提交和回滾。XA實現分布式事務的原理如下:
1. 准備階段:准備階段,每個資源管理器都會被輪訓一遍,事務管理器給每個資源管理器發送Prepare
消息,每個資源管理器要么直接返回失敗(如權限驗證失敗)或異常,要么在本地執行事務等等,但不Commoit
,處於Ready
狀態。
2. 提交階段:如果事務管理器收到了資源管理器的失敗信息(如異常、超時等),直接給每個資源管理器發送回滾(Rollback
)消息;否則,發送提交(Commit
)消息;資源管理器根據事務管理器的指令執行Commit
或者Rollback
操作,釋放所有事務處理過程中使用的鎖資源。(注意:必須在最后階段釋放鎖資源)
1、同步阻塞問題。執行過程中,所有參與節點都是事務阻塞型的。當參與者占有公共資源時,其他第三方節點訪問公共資源不得不處於阻塞狀態。
2、單點故障。由於協調者的重要性,一旦協調者發生故障。參與者會一直阻塞下去。尤其在第二階段,協調者發生故障,那么所有的參與者還都處於鎖定事務資源的狀態中,而無法繼續完成事務操作。(如果是協調者掛掉,可以重新選舉一個協調者,但是無法解決因為協調者宕機導致的參與者處於阻塞狀態的問題)
3、數據不一致。在二階段提交的階段二中,當協調者向參與者發送commit請求之后,發生了局部網絡異常或者在發送commit請求過程中協調者發生了故障,這回導致只有一部分參與者接受到了commit請求。而在這部分參與者接到commit請求之后就會執行commit操作。但是其他部分未接到commit請求的機器則無法執行事務提交。於是整個分布式系統便出現了數據部一致性的現象。
4、二階段無法解決的問題:協調者再發出commit消息之后宕機,而唯一接收到這條消息的參與者同時也宕機了。那么即使協調者通過選舉協議產生了新的協調者,這條事務的狀態也是不確定的,沒人知道事務是否被已經提交。
總的來說,XA協議比較簡單,而且一旦商業數據庫實現了XA協議,使用分布式事務的成本也比較低。但是,XA也有致命的缺點,那就是性能不理想,特別是在交易下單鏈路,往往並發量很高,XA無法滿足高並發場景。XA目前在商業數據庫支持的比較理想,在mysql數據庫中支持的不太理想,mysql的XA實現,沒有記錄prepare階段日志,主備切換回導致主庫與備庫數據不一致。許多nosql也沒有支持XA,這讓XA的應用場景變得非常狹隘。
其實也並非不用,例如在IBM大型機上基於CICS很多跨資源是基於XA協議實現的分布式事務,事實上XA也算分布式事務處理的規范了,但在為什么互聯網中很少使用,究其原因有以下幾個:
-
性能(阻塞性協議,增加響應時間、鎖時間、死鎖);
-
數據庫支持完善度(MySQL 5.7之前都有缺陷);
-
協調者依賴獨立的J2EE中間件(早期重量級Weblogic、Jboss、后期輕量級Atomikos、Narayana和Bitronix);
-
運維復雜,DBA缺少這方面經驗;
-
並不是所有資源都支持XA協議;
准確講XA是一個規范、協議,它只是定義了一系列的接口,只是目前大多數實現XA的都是數據庫或者MQ,所以提起XA往往多指基於資源層的底層分布式事務解決方案。其實現在也有些數據分片框架或者中間件也支持XA協議,畢竟它的兼容性、普遍性更好。
三、Seata AT(TXC) 模式
3.1基本概念
TC (Transaction Coordinator) - 事務協調者
維護全局和分支事務的狀態,驅動全局事務提交或回滾。
TM (Transaction Manager) - 事務管理器
定義全局事務的范圍:開始全局事務、提交或回滾全局事務。
RM (Resource Manager) - 資源管理器
管理分支事務處理的資源,與TC交談以注冊分支事務和報告分支事務的狀態,並驅動分支事務提交或回滾。
Seata 的全局事務處理過程,分為兩個階段:
①執行階段:執行分支事務,並保證執行結果滿足是可回滾的(Rollbackable)和持久化的(Durable)。
可回滾:根據 SQL 解析結果,記錄回滾日志
持久化:回滾日志和業務 SQL 在同一個本地事務中提交到數據庫
②完成階段:根據執行階段 結果形成的決議,應用通過 TM 發出的全局提交或回滾的請求給 TC,TC 命令 RM 驅動分支事務進行 Commit 或 Rollback。
分支提交:異步刪除回滾日志記錄
分支回滾:依據回滾日志進行反向補償更新
Seata 的所謂事務模式是指:運行在 Seata 全局事務框架下的分支事務的行為模式,准確地講應該叫作分支事務模式。不同的事務模式區別在於分支事務使用不同的方式達到全局事務兩個階段的目標。
回答以下兩個問題:
執行階段 :如何執行並保證 執行結果滿足是可回滾的(Rollbackable)和持久化的(Durable)。
完成階段: 收到TC的命令后,做到事務的回滾/提交
3.2AT模式工作流程
3.2.1第一階段:
在Seata的組件中,如果你想開啟分布式事務,那么就應該在你的業務入口或者事務發起入口加上@GlobalTransactional注解。數據源被代理后,通過被DataSourceProxy代理后,所執行的sql會被提取,解析,保存前鏡像后,再執行業務sql,再保存后鏡像,以便與后續出現異常,進行二階段的回滾操作。
3.2.2第二階段:
AT 模式二階段提交
二階段如果是提交的話,因為“業務 SQL”在一階段已經提交至數據庫,所以 Seata 框架只需將一階段保存的快照數據和行鎖刪掉,完成數據清理即可。
AT 模式二階段回滾
二階段如果是回滾的話,Seata 就需要回滾一階段已經執行的“業務 SQL”,還原業務數據。
回滾方式便是用“before image”還原業務數據;但在還原前要首先要校驗臟寫,對比“數據庫當前業務數據”和 “after image”,如果兩份數據完全一致就說明沒有臟寫,可以還原業務數據,如果不一致就說明有臟寫,出現臟寫就需要轉人工處理。
3.2.3:AT模式讀寫隔離實現方式
http://seata.io/zh-cn/docs/dev/mode/at-mode.html
3.2.4:完整的AT在Seata所制定的事務模式下的模型圖
3.2.5:Seata AT模式優劣:
①相比與其它分布式事務框架,Seata架構的亮點主要有幾個:
-
應用層基於SQL解析實現了自動補償,從而最大程度的降低業務侵入性;
-
將分布式事務中TC(事務協調者)獨立部署,負責事務的注冊、回滾;
-
通過全局鎖實現了寫隔離與讀隔離。
②性能損耗(純內存運算類的忽略不計):
一條Update的SQL,則需要全局事務xid獲取(與TC通訊)、before image(解析SQL,查詢一次數據庫)、after image(查詢一次數據庫)、insert undo log(寫一次數據庫)、before commit(與TC通訊,判斷鎖沖突),這些操作都需要一次遠程通訊RPC,而且是同步的。
另外undo log寫入時blob字段的插入性能也是不高的。每條寫SQL都會增加這么多開銷,粗略估計會增加5倍響應時間(二階段雖然是異步的,但其實也會占用系統資源,網絡、線程、數據庫)。
③補償型事務模式的問題:
本質上,Seata 已經支持的 3 大事務模式:AT、TCC、Saga 都是 補償型的。
補償型事務處理機制構建在事務資源 之上(要么在中間件層面,要么在應用層面),事務資源本身對分布式事務是無感知的。
事務資源對分布式事務的無感知存在一個根本性的問題:無法做到真正的全局一致性 。
比如,一條庫存記錄處在補償型 事務處理過程中由 100 扣減為 50。此時,倉庫管理員連接數據庫查詢統計庫存,就看到當前的 50之后事務因為異外回滾,庫存會被補償回滾為 100。顯然,倉庫管理員查詢統計到的 50 就是臟數據。
可以看到,補償型分布式事務機制因為不要求事務資源本身(如數據庫)的機制參與,所以無法保證從事務框架之外的全局視角的數據一致性。
④XA 的價值:
因為事務資源感知並參與分布式事務處理過程,所以事務資源(如數據庫)可以保障從任意視角對數據的訪問有效隔離,滿足全局數據一致性。
比如,上一節提到的庫存更新場景,XA 事務處理過程中,中間態數據庫存 50 由數據庫本身保證,是不會倉庫管理員的查詢統計看到的。(當然隔離級別需要讀已提交以上)
與同為業務無侵入 的 AT 模式比較:
首先,因為同樣運行在 Seata 定義的分布式事務框架下,XA 模式並沒有產生更多事務協調的通信開銷。
其次,並發事務間如果數據存在熱點,產生鎖沖突,這種情況在 AT 模式(默認使用全局鎖)下同樣存在的。
所以,在影響性能的兩個主要方面,XA 模式並不比 AT 模式有非常明顯的劣勢。AT 模式性能優勢主要在於:集中管理全局數據鎖,鎖的釋放不需要 RM 參與,釋放鎖非常快;另外,全局提交的事務完成階段異步化。
四、XA 模式 運行在 Seata 定義的事務框架
執行階段(Execute):
- XA start/XA end/XA prepare + SQL + 注冊分支
完成階段(Finish):
- XA commit/XA rollback
XA 模式的基本設計目標,兩個主要方面:
- 從 場景 上,滿足 全局一致性 的需求。
- 從 應用上,保持與 AT 模式一致的無侵入。
- 從 機制 上,適應分布式微服務架構的特點。
整體思路:
與 AT 模式相同的:以應用程序中 本地事務 的粒度,構建到 XA 模式的 分支事務。
通過數據源代理,在應用程序本地事務范圍外,在框架層面包裝 XA 協議的交互機制,把 XA 編程模型 透明化。
把 XA 的 2PC 拆開,在分支事務執行階段的末尾就進行 XA prepare,把 XA 協議完美融合到 Seata 的事務框架,減少一輪 RPC(遠程過程調用,簡單的理解是一個節點請求另一個節點提供的服務) 交互。
(注:XA start 需要 Xid 參數。這個 Xid 需要和 Seata 全局事務的 XID 和 BranchId 關聯起來,以便由 TC 驅動 XA 分支的提交或回滾。目前 Seata 的 BranchId 是在分支注冊過程,由 TC 統一生成的,所以 XA 模式分支注冊的時機需要在 XA start 之前。)
五:Seata AT與XA的優劣
第一點:由於上面我們總結了,其實AT就是一個自實現的XA事務,所以其實可以知道,AT在sql支持上是遠不及利用本地事務的XA模式,既然AT需要做sql解析那么背后的實現只能自己來解決,也就是靠Seata社區的貢獻者們來貢獻解決方案,這是一個長期性的關鍵問題,但是依然有很多用戶選擇了重寫sql來獲取AT事務模式的支持,在sql支持上XA無疑是完勝的;
第二點:隔離性,Seata AT模式通過解析sql獲取涉及的主鍵id,生成行鎖,也就是AT模式的隔離就是靠全局鎖來保證,粒度細至行級,鎖信息存儲在Seata-Server一側。XA模式的隔離性就是由本地數據庫保證,鎖存儲在各個本地數據庫中。由於XA模式一旦執行了prepare后,再也無法重入這個XA事務也無法跟其他XA事務共享鎖。因為XA協議僅是通過XID來start一個XA事務,本身它不存在所謂的分支事務說法,它本事就是一個XA事務而已,也就是說它只管它自己。
第三點:入侵性,通過我們以上的信息,其實可以發現,誰更底層,誰的入侵性更小,所以由數據庫自身所支持的XA模式來說,無疑入侵最小,使用成本最低。
上圖中,右側圖1是at模式運行時,圖2時xa模式運行時。可以很明顯,xa的阻塞帶來的性能下降時非常厲害的,特別是你的分支事務非常多,每個資源的釋放必須等到每個分支的數據庫去單獨釋放,后續的事務才能進入。雖然XA帶來的無侵入非常高,但是由於性能下降的程度太大,也就促使了AT的誕生,而現在AT,TCC,SAGA的模式的接受度也越來越高,這也正說明了開發者對性能的要求。AT可以看作時由Seata社區進行全方面優化,自研的XA模式,最大特點就是解決了XA模式的性能差的問題。
本博客內容借鑒:
https://www.cnblogs.com/zengkefu/p/5742617.html