導讀概述
隨着業務的快速發展,業務複雜度越來越高,大部分互聯網公司幾乎都會從單體走向分佈式,特別是轉向微服務架構,隨之而來就必然遇到分佈事務這個難題。
本文主要介紹一下Seata是如何保證數據一致性的,將從以下幾個方面介紹一下分佈式實現方案
1、介紹一下分佈式理論(此部分如果已經掌握了可以跳過,看下面部分分享)
2、什麼是Seata
3、Seata AT模式下如何解決分佈式事務,應用場景
4、Seata TCC模式下如何解決分佈式事務、應用場景、變種場景使用
5、Seata 下的Saga模式
本文以下單佔庫存案例爲主線分享Seata分佈式解決方案。
分佈式理論
什麼是事務?
指的就是一個操作單元,在這個操作單元中的所有操作最終要保持一致的行爲,要麼所有操作都成功,要麼所有的操作都被撤銷。
使用事務目的是什麼
說明白了就是保證數據的一致性。
什麼是本地事務呢
我們常常指的是關係型數據庫的事務,關係型數據庫事務四大特性:
Atomicity(原子性) :要麼都成功,要麼都失敗。
Consistency(一致性): 在事務開始之前和事務結束以後,數據庫的完整性沒有被破壞,完整性包括外鍵約束、應用定義的等約束不會被破壞。
Isolation(隔離性):數據庫允許多個併發事務同時對其數據進行讀寫和修改的能力,隔離性可以防止多個事務併發執行時由於交叉執行而導致數據的不一致。
Durability(持久性):事務處理結束後,對數據的修改就是永久的,即便系統故障也不會丟失。
優點:可以保證單機數據庫層面的數據一致性。
爲什麼要使用分佈式事務
分佈式事務在分佈式環境下,爲滿足可用性、性能與降級服務的需要,降低一致性與隔離性的要求,一方面遵循 BASE 理論
基本業務可用性(Basic Availability)
柔性狀態(Soft state)
最終一致性(Eventual consistency)
同樣的,分佈式事務也部分遵循 ACID 規範:
原子性:嚴格遵循
一致性:事務完成後的一致性嚴格遵循;事務中的一致性可適當放寬
隔離性:並行事務間不可影響;事務中間結果可見性允許安全放寬
持久性:嚴格遵循
需求是這樣的:
隨着業務發展在微服務架構下,【訂單服務】和【庫存服務】在兩個業務系統裏,同時分別調用對應的訂單DB和庫存DB,要求保證:要麼庫存和訂單下單一起成功,如果庫存佔用和訂單下單有一方失敗要麼就要回滾,保證數據的一致性。
此時場景數據庫級別的事務就有些捉襟見肘了,此時分佈式事務就派上用場了。
分佈式事務常見解決手段有哪些呢?
兩階段提交2PC、三階段提交3PC、TCC、SAGA、本地消息表、基於可靠消息保證最終一致、最大努力通知等方案。
由於分佈式事務方案,無法做到完全的ACID的保證,沒有一種完美的方案,能夠解決掉所有業務問題。因此在實際應用中,會根據業務的不同特性,選擇最適合的分佈式事務方案。
什麼是分佈式事務呢?
1、分佈式事務:
- 首先應用於分佈式系統場景,分佈式事務是指事務的參與者、支持事務的服務器、資源服務器以及事務管理器分別位於不同的分佈式系統的不同節點之上。
- 例如在電商系統中,下單接口通常會扣減庫存、添加優惠券、生成訂單 id, 而訂單服務與庫存、添加優惠券、下單接口的成功與否,不僅取決於本地的 db 操作,而且依賴第三方系統的結果,這時候分佈式事務就保證這些操作要麼全部成功,要麼全部失敗。本質上來說,分佈式事務就是爲了保證不同數據庫的數據一致性。
2、常見使用場景:
如用戶註冊送積分事務、創建訂單減庫存事務,銀行轉賬事務等都可以應用分佈式事務,分佈式事務就是爲了保證在分佈式場景下,數據操作的正確執行。
提到分佈式場景就繞不開CAP理論,三者只能滿足兩點,來解決分佈式場景的問題,那麼接下來先了解一下什麼是CAP。
CAP原則
CAP 原則又稱 CAP 定理,又被叫作布魯爾定理,指的是在一個分佈式系統中, Consistency(一致性)、 Availability(可用性)、Partition tolerance(分區容錯性),三者不可得兼。
CAP 原則的精髓就是要麼AP,要麼CP,要麼AC,但是不存在CAP。如果在某個分佈式系統中數據無副本,那麼系統必然滿足強一致性條件,因爲只有獨一數據,不會出現數據不一致的情況,此時C和P兩要素具備,但是如果系統發生了網絡分區狀況或者宕機,必然導致某些數據不可以訪問,此時可用性條件就不能被滿足,即在此情況下獲得了CP系統,但是CAP不可同時滿足。
即要保證數據一致還要保證可用,魚和熊掌不可兼得,那麼就要取捨,於是產生以下幾種一致性策略。
強一致性、弱一致性、最終一致性
1.強一致性
任何一次讀都能讀到某個數據的最近一次寫的數據。系統中的所有進程,看到的操作順序,都和全局時鐘下的順序一致。簡言之,在任意時刻,所有節點中的數據是一樣的。如:2PC、3PC、XA都屬於強一致性方案。
2.弱一致性
數據更新後,如果能容忍後續的訪問只能訪問到部分或者全部訪問不到,則是弱一致性。
3.最終一致性 不保證在任意時刻任意節點上的同一份數據都是相同的,但是隨着時間的遷移,不同節點上的同一份數據總是在向趨同的方向變化。簡單說,就是在一段時間後,節點間的數據會最終達到一致狀態。如:TCC和消息隊列模式、Saga模式屬於最終一致性的解決方案。
瞭解了分佈式事務中的強一致性和最終一致性理論,下面介紹幾種常見的分佈式事務的解決方案。
Seata 分佈式事務
什麼是Seata?
Seata是一款阿里巴巴開源的分佈式事務解決方案,致力於提供高性能和簡單易用的分佈式事務服務。Seata將爲用戶提供了 AT、TCC、SAGA 和 XA 事務模式,爲用戶打造一站式的分佈式解決方案。
什麼是Seata 的AT模式?
AT模式的特點就是對業務無入侵式,整體機制分二階段提交
- 基於支持本地 ACID 事務的關係型數據庫。
- Java 應用,通過 JDBC 訪問數據庫。
整體機制:兩階段協議的演變
一階段:業務數據和回滾日誌記錄在同一個本地事務中提交,釋放本地鎖和連接資源。
二階段:提交異步化,非常快速地完成;回滾通過一階段的回滾日誌進行反向補償。
Seata術語:
TC (Transaction Coordinator) 事務協調者 :維護全局和分支事務的狀態,驅動全局事務提交或回滾。
TM (Transaction Manager) 事務管理器:定義全局事務的範圍:開始全局事務、提交或回滾全局事務。
RM (Resource Manager) 資源管理器:管理分支事務處理的資源,與TC交談以註冊分支事務和報告分支事務的狀態,並驅動分支事務提交或回滾。
在 AT 模式下,用戶只需關注自己的業務SQL,用戶的業務SQL 作爲一階段,Seata 框架會自動生成事務的二階段提交和回滾操作。
Seata 的AT模式使用場景
怎麼理解AT模式呢,咱們以佔用庫存和創建訂單爲例:
TM端功能包括【佔用庫存】和【創建訂單】的操作,使用@GlobalTransaction進行全局事務開啓、提交、回滾。
TM開始RPC調用遠程服務調用分支【庫存】服務和【創建訂單】服務。
RM端seata-client通過擴展DataSourceProxy,實現自動生成undo_log與TC上報。
TM告知TC提交/回滾全局事務。
TC通知RM各自執行commit/rollback操作,同時清除undo_log。
下面爲你介紹一下AT模式下的佔庫存和生單流程拆解如下:
第一階段(佔用庫存過程)
- TM開啓全局事務。
- 庫存註冊分支事務,獲取全局鎖。
- 本地事務提交:插入前鏡像beforeImage->undolog日誌,佔用庫存,插入後鏡像afterImage->undolog日誌,生成undo log一併提交,將本地事務提交的結果上報給TC。
第一階段(創建訂單過程)
- 創建訂單註冊分支事務,獲取全局鎖。
- 本地事務提交:插入前鏡像beforeImage->undolog日誌,創建訂單成功,插入後鏡像afterImage->undolog日誌,生成undo log一併提交,將本地事務提交的結果上報給TC。
第二階段:提交或回滾
- 場景1.如果佔用庫存成功 訂單創建成功,則提交全局事務結束,刪除本地undolog記錄
- 場景2.如果佔用庫存或訂單創建某一方失敗,則反向操作undolog回滾日誌,刪除本地undlog記錄。
AT模式下如何保證數據一致的?
一階段提交如何保持一致性?
TM:method下單服務方法執行時,由於該方法具有@GlobalTranscational標誌,該TM會向TC發起全局事務,生成XID(全局鎖)。
RM寫表的過程,Seata 會攔截業務SQL,首先解析 SQL 語義,在業務數據被更新前,將其保存成before image,然後執行業務SQL,在業務數據更新之後,再將其保存成after image,最後生成行鎖。以上操作全部在一個數據庫事務內完成,這樣保證了一階段操作的原子性。
二階段如何保持一致性的?
因爲“業務 SQL”在一階段已經提交至數據庫, 所以 Seata 框架只需將一階段保存的快照數據和行鎖刪掉,完成數據清理即可。
正常:TM執行成功,通知TC全局提交,TC此時通知所有的RM提交成功,刪除UNDO_LOG回滾日誌。
異常階段如何保持一致性?
異常:TM執行失敗,通知TC全局回滾,TC此時通知所有的RM進行回滾,根據undo_log反向操作,使用before image還原業務數據,刪除undo_log,但在還原前要首先要校驗髒寫,對比“數據庫當前業務數據”和 “after image”,如果兩份數據完全一致就說明沒有髒寫,可以還原業務數據,如果不一致就說明有髒寫,出現髒寫就需要轉人工處理了。
什麼是Seata 的TCC模式?
TCC 模式需要用戶根據自己的業務場景實現 Try、Confirm 和 Cancel 三個操作;事務發起方在一階段執行 Try 方式,在二階段提交執行 Confirm 方法,二階段回滾執行 Cancel 方法
- 一階段 :Try 資源的檢測和預留。
- 二階段 commit 或 rollback回滾行爲。 Confirm:執行的業務操作提交;要求 Try 成功 Confirm 一定要能成功。 Cancel:預留資源釋放。
相應的,TCC模式(try-confirm-cancel),不依賴於底層數據資源的事務支持:
- 一階段 prepare 行爲:調用自定義的 prepare 邏輯,Try 資源的檢測和預留。
- 二階段 commit 行爲:調用自定義的 commit 邏輯,執行的業務操作提交;要求 Try 成功Confirm 一定要能成功,失敗則重試。
- 二階段 rollback 行爲:調用自定義的 rollback 邏輯,預留資源釋放。
所謂 TCC 模式,是指支持把自定義的分支事務納入到全局事務的管理中。
Seata 的TCC模式使用場景
怎麼理解TCC模式呢,咱們還以下單扣庫存爲例,Try 階段去佔庫存,Confirm 階段則實際扣庫存,如果庫存扣減失敗 Cancel 階段進行回滾,釋放庫存。
TCC不存在資源阻塞的問題,因爲每個方法都直接進行事務的提交,一旦出現異常通過則 Cancel 來進行回滾補償,這也就是常說的補償性事務,拆解流程請看下圖:
佔用庫存storage TCC流程圖如下:
- try:凍結庫存操作。
- confirm:佔用庫存操作。
- cancel:回滾事務,直接調用回滾庫存接口。
訂單系統 order TCC流程圖如下:
- try: 新增訂單-狀態更新成【創建中】。
- confirm: try成功,下單操作成功,訂單狀態由【創建中】更新成【創建成功】。
- cancel: 回滾事務,刪除該訂單記錄。
小結:
當佔用庫存成功同時訂單下成功時,提交全局事務,當接入TCC模式,最重要的事情就是考慮如何將業務模型拆成2階段,實現成TCC的3個方法,並且保證Try成功Confirm一定能成功。相對於AT模式,TCC模式對業務代碼有一定的侵入性,但是TCC模式無AT模式的全局行鎖,TCC性能會比AT模式高很多。
Seata 的TCC模式變種實現方案?
在說TCC變種簡化變種方案前,先說一說現實的業務場景:
現實業務並不一定如我們所願,在業務已成規模,訂單系統 和 庫存系統,都是在分佈式場景,各自的業務接口,各自的DB表,如何保證數據一致呢?
已實現邏輯採用業務代碼異常調用返向補償實現的,僞代碼如下:
try{
佔用庫存()
try{
下單方法()
}cach(){
釋放庫存();
取消訂單();
}
}cach(){
釋放庫存();
}
庫存服務:1.佔用庫存接口 2.釋放庫存接口。
訂單服務:1.下單接口 2.取消訂單接口。
流程圖如下:
基於現有邏輯如何改造成分佈式事務實現方案呢 ?
方案1:採用AT模式,需要做的工作:
在業務庫 訂單數據庫和庫存數據庫 分別創建undo_log日誌。
RM端(庫存業務和訂單業務代碼)seata-client通過擴展DataSourceProxy,實現自動生成undo_log與tc上報,需要業務嵌入代碼實現。
缺點:需要對庫存業務庫和訂單業務庫存分別存加 undo_log表,事務相對比較長。
方案2:採用Seata TCC模式
try階段:提供凍結庫存方法 和 預佔用訂單方法 需要改造庫存表和訂單表提供中間狀態標識。
confirm階段:提供佔用庫存方法和下單成功方法。
cancel階段:提供返向 釋放庫存和取消訂單方法。
缺點:業務代碼要寫庫存鎖定狀態業務邏輯+訂單創建中的中間態。
從上面兩個方案不難看出都有一個通病,都需要業務代碼改造,侵入強,耦合度高,能否有一種,即不需要業務代碼改造成本又低的方案呢?
方案3:接下來的方案是在TCC模式上做了些簡化:
即在try階段:直接佔庫存成功和下單成功。
在confirm階段:不做業務處理,直接返回true,認爲是成功的。
rollback階段:做返回的取消操作即可。
缺點:浪費了一次commit空提交 IO交互。
優點:業務侵入性小,可以適應現有業務場景,偶合度低。
簡化版本的TCC實現方案的時序圖如下:
Seata TCC分佈式場景下如何保證冪等呢?
首先說一下什麼是冪等?
冪等意思:通常指對同一個系統,使用同樣的條件,一次請求和重複的多次請求對系統資源的影響是一致的.因爲網絡抖動或擁堵可能會超時,事務管理器會對資源進行重試操作,所以很可能一個業務操作會被重複調用,爲了不因爲重複調用而多次佔用資源,造成業務髒數據,就需要保證冪等性。
如何保證冪等呢?
此時需要對服務設計時進行冪等控制,通常我們可以用事務xid或業務主鍵判重來控制,或採用業務唯一標識來做冪等,總之還是需要業務自己來保證冪等的。
Seata下的Saga模式?
1.Saga是一種補償協議,Saga 理論出自 Hector & Kenneth 1987發表的論文 Sagas,其核心思想是將長事務拆分爲多個本地短事務。
解決了什麼問題?
2.Saga模式是seata提供的長事務解決方案,在Saga模式中,業務流程中每個參與者都提交本地事務,當出現某一個參與者失敗則補償前面已經成功的參與者,一階段正向服務和二階段補償服務都由業務開發實現。
如圖:T1T3都是正向的業務流程,都對應着一個衝正逆向補償操作C1C3
適用場景:
- 業務流程長、業務流程多。
- 參與者包含其它公司或遺留系統服務,無法提供 TCC 模式要求的三個接口。
優勢:
- 一階段提交本地事務,無鎖,高性能。
- 事件驅動架構,參與者可異步執行,高吞吐。
- 補償服務易於實現。
缺點:
Saga 模式由於一階段已經提交本地數據庫事務,且沒有進行“預留”動作,所以不能保證隔離性。後續會講到對於缺乏隔離性的應對措施。
seata分佈式事務常見問題
常見問題答疑
問:Seata框架如何來保證事務的隔離性的?
答:因seata一階段本地事務已提交,爲防止其他事務髒讀髒寫需要加強隔離。
1.讀隔離:Seata(AT 模式)的默認全局隔離級別是讀未提交,必須要求全局的讀已提交 ,目前 Seata 的方式是通過select語句加for update代理方法增加@GlobalLock+@Transactional或@GlobalTransaction實現。
2.寫隔離:
一階段本地事務提交前,需要確保先拿到全局鎖 。
拿不到全局鎖 ,不能提交本地事務。
拿 全局鎖 的嘗試被限制在一定範圍內,超出範圍將放棄,並回滾本地事務,釋放本地鎖。
問:髒數據回滾失敗如何處理?
答:1.髒數據需手動處理,根據日誌提示修正數據或者將對應undo刪除(可自定義實現FailureHandler做郵件通知或其他人工介入操作)。
2.關閉回滾時undo鏡像校驗,不推薦該方案。
注意事項:建議事前做好隔離保證無髒數據。
問:Seata支持哪些RPC框架?
答:1. AT 模式支持:Dubbo、Spring Cloud、Motan、GRPC 和 Sofa-RPC。
2. TCC 模式支持:Dubbo、Spring Cloud和Sofa-RPC。
問:Seata現階段支持哪些分庫分表解決方案?
答:現階段只支持ShardingSphere。
問:Seata目前支持高可用嗎?
答:支持
1.tc使用db模式共享全局事務會話信息,註冊中心使用非file的seata支持的第三方註冊中心。
2.註冊中心包含:eureka、consul、nacos、etcd、zookeeper、sofa、redis、file (直連)。
小結:
在當前的技術發展階段,不存一個分佈式事務處理機制可以完美滿足所有場景的需求。一致性、可靠性、易用性、性能等諸多方面的系統設計約束,需要用不同的事務處理機制去滿足。
目前使用的流行度情況是:AT>TCC > Saga,Seata 項目最核心的價值在於:構建一個全面解決分佈式事務問題的標準化平臺。基於 Seata,上層應用架構可以根據實際場景的需求,靈活選擇合適的分佈式事務解決方案。
分佈式方案對比
Seata針對不同的業務場景提供了四種不同的事務模式,對比如下:
AT模式:AT 模式的一階段、二階段提交和回滾(藉助undo_log表來實現)均由 Seata 框架自動生成,用戶只需編寫“業務SQL”,便能輕鬆接入分佈式事務,AT 模式是一種對業務無任何侵入的分佈式事務解決方案。
TCC模式:相對於 AT 模式,TCC 模式對業務代碼有一定的侵入性,但是 TCC 模式無 AT 模式的全局行鎖,TCC 性能會比 AT模式高很多。適用於核心系統等對性能有很高要求的場景。
SAGA模式:Sage 是長事務解決方案,事務驅動,使用那種存在流程審覈的業務場景。
XA模式:XA模式是分佈式強一致性的解決方案,但性能低而使用較少。
後記
本文介紹了分佈式事務的一些基礎理論,主要對Seata分佈式事務方案進行了講解,在文章的後半部分主要給出了各種方案的常用場景。分佈式事務本身就是一個技術難題,業務中具體使用哪種方案還是需要根據自身業務特點自行選擇,每種方案在實際執行過程中需要考慮的點都非常多,複雜度較大,所以在非必要的情況下,分佈式事務能不用就儘量不用,希望對大家有所幫助。