前言
本文小新為大家?guī)?分布式事務(wù)組件Seata 相關(guān)知識,具體內(nèi)容包括分布式事務(wù)簡介
(包括:事務(wù)簡介
,本地事務(wù)
,分布式事務(wù)典型場景
,分布式事務(wù)理論基礎(chǔ)
,分布式事務(wù)解決方案
),分布式事務(wù)Seata使用
(包括:Seata是什么
,Seata的三大角色
,Seata的設(shè)計思路
,Seata的設(shè)計亮點
,Seata存在的問題
),Seata快速開始
(包括:Seata Server(TC)環(huán)境搭建
,業(yè)務(wù)系統(tǒng)集成Client
)等進行詳盡介紹~
不積跬步,無以至千里;不積小流,無以成江海。每天進步一點點,在成為強者的路上,小新與大家共同成長!
??博主主頁:小新要變強 的主頁
??Java全棧學(xué)習(xí)路線可參考:【Java全棧學(xué)習(xí)路線】最全的Java學(xué)習(xí)路線及知識清單,Java自學(xué)方向指引,內(nèi)含最全Java全棧學(xué)習(xí)技術(shù)清單~
??算法刷題路線可參考:算法刷題路線總結(jié)與相關(guān)資料分享,內(nèi)含最詳盡的算法刷題路線指南及相關(guān)資料分享~
??Java微服務(wù)開源項目可參考:企業(yè)級Java微服務(wù)開源項目(開源框架,用于學(xué)習(xí)、畢設(shè)、公司項目、私活等,減少開發(fā)工作,讓您只關(guān)注業(yè)務(wù)!)
??本文上接:Spring Cloud Alibaba全家桶(八)——Sentinel規(guī)則持久化
目錄
一、分布式事務(wù)簡介
1??事務(wù)簡介
事務(wù)(Transaction) 是訪問并可能更新數(shù)據(jù)庫中各種數(shù)據(jù)項的一個程序執(zhí)行單元(unit)。在關(guān)系數(shù)據(jù)庫中,一個事務(wù)由一組SQL語句組成。事務(wù)應(yīng)該具有4個屬性:原子性、一致性、隔離性、持久性。 這四個屬性通常稱為ACID特性。
- 原子性(atomicity): 個事務(wù)是一個不可分割的工作單位,事務(wù)中包括的諸操作要么都做,要么都不做。
- 一致性(consistency): 事務(wù)必須是使數(shù)據(jù)庫從一個一致性狀態(tài)變到另一個一致性狀態(tài),事務(wù) 的中間狀態(tài)不能被觀察到的。
- 隔離性(isolation): 一個事務(wù)的執(zhí)行不能被其他事務(wù)干擾。即一個事務(wù)內(nèi)部的操作及使用的數(shù) 據(jù)對并發(fā)的其他事務(wù)是隔離的,并發(fā)執(zhí)行的各個事務(wù)之間不能互相干擾。隔離性又分為四個級別:讀未提交(read uncommitted)、讀已提交(read committed,解決臟讀)、可重復(fù)讀(repeatable read,解決虛讀)、串行化(serializable,解決幻讀)。
- 持久性(durability): 持久性也稱永久性(permanence),指一個事務(wù)一旦提交,它對數(shù)據(jù)庫 中數(shù)據(jù)的改變就應(yīng)該是永久性的。接下來的其他操作或故障不應(yīng)該對其有任何影響。任何事務(wù)機制在實現(xiàn)時,都應(yīng)該考慮事務(wù)的ACID特性,包括:本地事務(wù)、分布式事務(wù),及時不能都很好的滿足,也要考慮支持到什么程度。
2??本地事務(wù)
大多數(shù)場景下,我們的應(yīng)用都只需要操作單一的數(shù)據(jù)庫,這種情況下的事務(wù)稱之為本地事務(wù)
(Local Transaction)。本地事務(wù)的ACID特性是數(shù)據(jù)庫直接提供支持。本地事務(wù)應(yīng)用架構(gòu)如下所
示:
在JDBC編程中,我們通過java.sql.Connection
對象來開啟、關(guān)閉或者提交事務(wù)。代碼如下所
示:
Connection conn = ... //獲取數(shù)據(jù)庫連接
conn.setAutoCommit(false); //開啟事務(wù)
try{
//...執(zhí)行增刪改查sql
conn.commit(); //提交事務(wù)
}catch (Exception e) {
conn.rollback();//事務(wù)回滾
}finally{
conn.close();//關(guān)閉鏈接
}
3??分布式事務(wù)典型場景
當(dāng)下互聯(lián)網(wǎng)發(fā)展如火如荼,絕大部分公司都進行了數(shù)據(jù)庫拆分和服務(wù)化(SOA)。在這種情況下,完
成某一個業(yè)務(wù)功能可能需要橫跨多個服務(wù),操作多個數(shù)據(jù)庫。這就涉及到到了分布式事務(wù),用需要操作的資源位于多個資源服務(wù)器上,而應(yīng)用需要保證對于多個資源服務(wù)器的數(shù)據(jù)的操作,要么全部成功,要么全部失敗。本質(zhì)上來說,分布式事務(wù)就是為了保證不同資源服務(wù)器的數(shù)據(jù)一致性。
典型的分布式事務(wù)場景:
- 跨庫事務(wù)
跨庫事務(wù)指的是,一個應(yīng)用某個功能需要操作多個庫,不同的庫中存儲不同的業(yè)務(wù)數(shù)據(jù)。筆者見過
一個相對比較復(fù)雜的業(yè)務(wù),一個業(yè)務(wù)中同時操作了9個庫。下圖演示了一個服務(wù)同時操作2個庫的情
況:
- 分庫分表
通常一個庫數(shù)據(jù)量比較大或者預(yù)期未來的數(shù)據(jù)量比較大,都會進行水平拆分,也就是分庫分表。如
下圖,將數(shù)據(jù)庫B拆分成了2個庫:
對于分庫分表的情況,一般開發(fā)人員都會使用一些數(shù)據(jù)庫中間件來降低sql操作的復(fù)雜性。如,對于sql:insert into user(id,name) values (1,"張三"),(2,"李四")
。這條sql是操作單庫的語法,單庫情況下,可以保證事務(wù)的一致性。
但是由于現(xiàn)在進行了分庫分表,開發(fā)人員希望將1號記錄插入分庫1,2號記錄插入分庫2。所以數(shù)據(jù)庫中間件要將其改寫為2條sql,分別插入兩個不同的分庫,此時要保證兩個庫要不都成功,要不都
失敗,因此基本上所有的數(shù)據(jù)庫中間件都面臨著分布式事務(wù)的問題。
- 服務(wù)化
微服務(wù)架構(gòu)是目前一個比較一個比較火的概念。例如上面筆者提到的一個案例,某個應(yīng)用同時操作了9個庫,這樣的應(yīng)用業(yè)務(wù)邏輯必然非常復(fù)雜,對于開發(fā)人員是極大的挑戰(zhàn),應(yīng)該拆分成不同的獨立服務(wù),以簡化業(yè)務(wù)邏輯。拆分后,獨立服務(wù)之間通過RPC框架來進行遠(yuǎn)程調(diào)用,實現(xiàn)彼此的通信。下圖演示了一個3個服務(wù)之間彼此調(diào)用的架構(gòu):
Service A完成某個功能需要直接操作數(shù)據(jù)庫,同時需要調(diào)用Service B和Service C,而Service B 又同時操作了2個數(shù)據(jù)庫,Service C也操作了一個庫。需要保證這些跨服務(wù)的對多個數(shù)據(jù)庫的操作要不都成功,要不都失敗,實際上這可能是 典型的分布式事務(wù)場景。
小結(jié): 上述討論的分布式事務(wù)場景中,無一例外的都直接或者間接的操作了多個數(shù)據(jù)庫。如何保證事務(wù)的ACID特性,對于分布式事務(wù)實現(xiàn)方案而言,是非常大的挑戰(zhàn)。同時,分布式事務(wù)實現(xiàn)方案還必須要考慮性能的問題,如果為了嚴(yán)格保證ACID特性,導(dǎo)致性能嚴(yán)重下降,那么對于一些要求快速響應(yīng)的業(yè)務(wù),是無法接受的。
4??分布式事務(wù)理論基礎(chǔ)
解決分布式事務(wù),也有相應(yīng)的規(guī)范和協(xié)議。分布式事務(wù)相關(guān)的協(xié)議有2PC、3PC。
由于三階段提交協(xié)議3PC非常難實現(xiàn),目前市面主流的分布式事務(wù)解決方案都是2PC協(xié)議。2PC具有普適性——協(xié)議一樣的存在,目前絕大多數(shù)分布式解決方案都是以兩階段提交協(xié)議2PC為基礎(chǔ)的。
2PC(兩階段提交,Two-Phase Commit)
顧名思義,分為兩個階段:Prepare 和 Commit。
??(1)Prepare:提交事務(wù)請求
基本流程如下圖:
- (1)詢問協(xié)調(diào)者向所有參與者發(fā)送事務(wù)請求,詢問是否可執(zhí)行事務(wù)操作,然后等待各個參與者的響應(yīng)。
- (2)執(zhí)行各個參與者接收到協(xié)調(diào)者事務(wù)請求后,執(zhí)行事務(wù)操作(例如更新一個關(guān)系型數(shù)據(jù)庫表中的記錄),并將Undo 和 Redo 信息記錄事務(wù)日志中。
- (3)響應(yīng)如果參與者成功執(zhí)行了事務(wù)并寫入 Undo 和 Redo 信息,則向協(xié)調(diào)者返回 YES 響應(yīng),否則返回 NO響應(yīng)。當(dāng)然,參與者也可能宕機,從而不會返回響應(yīng)。
??(2)Commit:執(zhí)行事務(wù)提交
執(zhí)行事務(wù)提交分為兩種情況,正常提交和回退。
正常提交事務(wù)
流程如下圖:
- (1)commit 請求 協(xié)調(diào)者向所有參與者發(fā)送 Commit 請求。
- (2)事務(wù)提交 參與者收到 Commit 請求后,執(zhí)行事務(wù)提交,提交完成后釋放事務(wù)執(zhí)行期占用的所有資源。
- (3)反饋結(jié)果 參與者執(zhí)行事務(wù)提交后向協(xié)調(diào)者發(fā)送 Ack 響應(yīng)。
- (4)完成事務(wù) 接收到所有參與者的 Ack 響應(yīng)后,完成事務(wù)提交。
中斷事務(wù)
在執(zhí)行 Prepare 步驟過程中,如果某些參與者執(zhí)行事務(wù)失敗、宕機或與協(xié)調(diào)者之間的網(wǎng)絡(luò)中斷,那么協(xié)調(diào)者就無法收到所有參與者的 YES 響應(yīng),或者某個參與者返回了 No 響應(yīng),此時,協(xié)調(diào)者就會進入回退流程,對事務(wù)進行回退。流程如下圖紅色部分(將 Commit 請求替換為紅色的 Rollback 請求):
- (1)rollback 請求 協(xié)調(diào)者向所有參與者發(fā)送 Rollback 請求。
- (2)事務(wù)回滾 參與者收到 Rollback 后,使用 Prepare 階段的 Undo 日志執(zhí)行事務(wù)回滾,完成后釋放事務(wù)執(zhí)行期占用的所有資源。
- (3)反饋結(jié)果 參與者執(zhí)行事務(wù)回滾后向協(xié)調(diào)者發(fā)送 Ack 響應(yīng)。
- (4)中斷事務(wù) 接收到所有參與者的 Ack 響應(yīng)后,完成事務(wù)中斷。
2PC 存在的問題:
- (1)同步阻塞 參與者在等待協(xié)調(diào)者的指令時,其實是在等待其他參與者的響應(yīng),在此過程中,參與者是無法進行其他操作的,也就是阻塞了其運行。
倘若參與者與協(xié)調(diào)者之間網(wǎng)絡(luò)異常導(dǎo)致參與者一直收不到協(xié)調(diào)者信息,那么會導(dǎo)致參與者一直阻塞下去。 - (2)單點 在 2PC 中,一切請求都來自協(xié)調(diào)者,所以協(xié)調(diào)者的地位是至關(guān)重要的,如果協(xié)調(diào)者宕機,那么就會使參與者一直阻塞并一直占用事務(wù)資源。如果協(xié)調(diào)者也是分布式,使用選主方式提供服務(wù),那么在一個協(xié)調(diào)者掛掉后,可以選取另一個協(xié)調(diào)者繼續(xù)后續(xù)的服務(wù),可以解決單點問題。但是,新協(xié)調(diào)者無法知道上一個事務(wù)的全部狀態(tài)信息(例如已等待 Prepare 響應(yīng)的時長等),所以也無法順利處理上一個事務(wù)。
- (3)數(shù)據(jù)不一致 Commit 事務(wù)過程中 Commit 請求/Rollback 請求可能因為協(xié)調(diào)者宕機或協(xié)調(diào)者與參與者網(wǎng)絡(luò)問題丟失,那么就導(dǎo)致了部分參與者沒有收到 Commit/Rollback 請求,而其他參與者則正常收到執(zhí)行了Commit/Rollback 操作,沒有收到請求的參與者則繼續(xù)阻塞。這時,參與者之間的數(shù)據(jù)就不再一致了。當(dāng)參與者執(zhí)行 Commit/Rollback 后會向協(xié)調(diào)者發(fā)送 Ack,然而協(xié)調(diào)者不論是否收到所有的參與者的 Ack,該事務(wù)也不會再有其他補救措施了,協(xié)調(diào)者能做的也就是等待超時后像事務(wù)發(fā)起者返回一個“我不確定該事務(wù)是否成功”。
- (4)環(huán)境可靠性依賴 協(xié)調(diào)者 Prepare 請求發(fā)出后,等待響應(yīng),然而如果有參與者宕機或與協(xié)調(diào)者之間的網(wǎng)絡(luò)中斷,都會導(dǎo)致協(xié)調(diào)者無法收到所有參與者的響應(yīng),那么在 2PC 中,協(xié)調(diào)者會等待一定時間,然后超時后,會觸發(fā)事務(wù)中斷,在這個過程中,協(xié)調(diào)者和所有其他參與者都是出于阻塞的。這種機制對網(wǎng)絡(luò)問題常見的現(xiàn)實環(huán)境來說太苛刻了。
5??分布式事務(wù)解決方案
常見分布式事務(wù)解決方案:
- 1、 seata 阿里分布式事務(wù)框架
- 2、 消息隊列
- 3、 saga
- 4、 XA
他們有一個共同點,都是“兩階段(2PC)”。“兩階段”是指完成整個分布式事務(wù),劃分成兩個步驟完成。
實際上,這四種常見的分布式事務(wù)解決方案,分別對應(yīng)著分布式事務(wù)的四種模式:AT、TCC、Saga、XA;四種分布式事務(wù)模式,都有各自的理論基礎(chǔ),分別在不同的時間被提出;每種模式都有它的適用場景,同樣每個模式也都誕生有各自的代表產(chǎn)品;而這些代表產(chǎn)品,可能就是我們常見的(全局事務(wù)、基于可靠消息、 大努力通知、TCC)。
下面我們分別來看4種模式(AT、TCC、Saga、XA)的分布式事務(wù)實現(xiàn)。
??(1)AT模式(auto transcation)
AT 模式是一種無侵入的分布式事務(wù)解決方案。
阿里seata框架,實現(xiàn)了該模式。
在 AT 模式下,用戶只需關(guān)注自己的“業(yè)務(wù) SQL”,用戶的 “業(yè)務(wù) SQL” 作為一階段,Seata 框架會自動生成事務(wù)的二階段提交和回滾操作。
AT 模式如何做到對業(yè)務(wù)的無侵入 :
- 一階段
在一階段,Seata 會攔截“業(yè)務(wù) SQL”,首先解析 SQL 語義,找到“業(yè)務(wù) SQL”要更新的業(yè)務(wù)數(shù)據(jù),在業(yè)務(wù)數(shù)據(jù)被更新前,將其保存成“before image”,然后執(zhí)行“業(yè)務(wù) SQL”更新業(yè)務(wù)數(shù)據(jù),在業(yè)務(wù)數(shù)據(jù)更新之后,再將其保存成“after image”,最后生成行鎖。以上操作全部在一個數(shù)據(jù)庫事務(wù)內(nèi)完成,這樣保證了一階段操作的原子性。
- 二階段提交
二階段如果是提交的話,因為“業(yè)務(wù) SQL”在一階段已經(jīng)提交至數(shù)據(jù)庫, 所以 Seata 框架只需將一階段保存的快照數(shù)據(jù)和行鎖刪掉,完成數(shù)據(jù)清理即可。
- 二階段回滾
二階段如果是回滾的話,Seata 就需要回滾一階段已經(jīng)執(zhí)行的“業(yè)務(wù) SQL”,還原業(yè)務(wù)數(shù)據(jù)?;貪L方式便是用“before image”還原業(yè)務(wù)數(shù)據(jù);但在還原前要首先要校驗臟寫,對比“數(shù)據(jù)庫當(dāng)前業(yè)務(wù)數(shù)據(jù)”和 “after image”,如果兩份數(shù)據(jù)完全一致就說明沒有臟寫,可以還原業(yè)務(wù)數(shù)據(jù),如果不一致就說明有臟寫,出現(xiàn)臟寫就需要轉(zhuǎn)人工處理。
AT 模式的一階段、二階段提交和回滾均由 Seata 框架自動生成,用戶只需編寫“業(yè)務(wù) SQL”,便能
輕松接入分布式事務(wù),AT 模式是一種對業(yè)務(wù)無任何侵入的分布式事務(wù)解決方案。
??(2)TCC 模式(Try、Confirm and Cancel)
TCC 模式特點:
- (1)侵入性比較強, 并且得自己實現(xiàn)相關(guān)事務(wù)控制邏輯
- (2)在整個過程基本沒有鎖,性能更強
TCC 模式需要用戶根據(jù)自己的業(yè)務(wù)場景實現(xiàn) Try、Confirm 和 Cancel 三個操作;事務(wù)發(fā)起方在一階段執(zhí)行 Try 方式,在二階段提交執(zhí)行 Confirm 方法,二階段回滾執(zhí)行 Cancel 方法。
TCC 三個方法描述:
- Try:資源的檢測和預(yù)留;
- Confirm:執(zhí)行的業(yè)務(wù)操作提交;要求 Try 成功 Confirm 一定要能成功;
- Cancel:預(yù)留資源釋放;
對于TCC模式的實踐過程中要注意以下事項:
- (1)業(yè)務(wù)模型分2階段設(shè)計
用戶接入 TCC 模式,最重要的事情就是考慮如何將業(yè)務(wù)模型拆成 2 階段,實現(xiàn)成 TCC 的 3 個方
法,并且保證 Try 成功 Confirm 一定能成功。相對于 AT 模式,TCC 模式對業(yè)務(wù)代碼有一定的侵入
性,但是 TCC 模式無 AT 模式的全局行鎖,TCC 性能會比 AT 模式高很多。
- (2)允許空回滾
Cancel 接口設(shè)計時需要允許空回滾。在 Try 接口因為丟包時沒有收到,事務(wù)管理器會觸發(fā)回滾,這
時會觸發(fā) Cancel 接口,這時 Cancel 執(zhí)行時發(fā)現(xiàn)沒有對應(yīng)的事務(wù) xid 或主鍵時,需要返回回滾成功。讓事務(wù)服務(wù)管理器認(rèn)為已回滾,否則會不斷重試,而 Cancel 又沒有對應(yīng)的業(yè)務(wù)數(shù)據(jù)可以進行回滾。
- (3)防懸掛控制
懸掛的意思是:Cancel 比 Try 接口先執(zhí)行,出現(xiàn)的原因是 Try 由于網(wǎng)絡(luò)擁堵而超時,事務(wù)管理器生成回滾,觸發(fā) Cancel 接口,而 終又收到了 Try 接口調(diào)用,但是 Cancel 比 Try 先到。按照前面允許空回滾的邏輯,回滾會返回成功,事務(wù)管理器認(rèn)為事務(wù)已回滾成功,則此時的 Try 接口不應(yīng)該執(zhí)行,否則會產(chǎn)生數(shù)據(jù)不一致,所以我們在 Cancel 空回滾返回成功之前先記錄該條事務(wù) xid 或業(yè)務(wù)主鍵,標(biāo)識這條記錄已經(jīng)回滾過,Try 接口先檢查這條事務(wù)xid或業(yè)務(wù)主鍵如果已經(jīng)標(biāo)記為回滾成功過,則不執(zhí)行 Try 的業(yè)務(wù)操作。
- (4)冪等控制
冪等性的意思是:對同一個系統(tǒng),使用同樣的條件,一次請求和重復(fù)的多次請求對系統(tǒng)資源的影響是一致的。因為網(wǎng)絡(luò)抖動或擁堵可能會超時,事務(wù)管理器會對資源進行重試操作,所以很可能一個業(yè)務(wù)操作會被重復(fù)調(diào)用,為了不因為重復(fù)調(diào)用而多次占用資源,需要對服務(wù)設(shè)計時進行冪等控制,通常我們可以用事務(wù) xid 或業(yè)務(wù)主鍵判重來控制。
??(3)saga模式
Saga 理論出自 Hector & Kenneth 1987發(fā)表的論文 Sagas。
saga模式的實現(xiàn),是長事務(wù)解決方案。
Saga 是一種補償協(xié)議,在 Saga 模式下,分布式事務(wù)內(nèi)有多個參與者,每一個參與者都是一個沖正
補償服務(wù),需要用戶根據(jù)業(yè)務(wù)場景實現(xiàn)其正向操作和逆向回滾操作。
如圖:T1-T3都是正向的業(yè)務(wù)流程,都對應(yīng)著一個沖正逆向操作C1-C3
分布式事務(wù)執(zhí)行過程中,依次執(zhí)行各參與者的正向操作,如果所有正向操作均執(zhí)行成功,那么分布式事務(wù)提交。如果任何一個正向操作執(zhí)行失敗,那么分布式事務(wù)會退回去執(zhí)行前面各參與者的逆向回滾操作,回滾已提交的參與者,使分布式事務(wù)回到初始狀態(tài)。
Saga 正向服務(wù)與補償服務(wù)也需要業(yè)務(wù)開發(fā)者實現(xiàn)。因此是業(yè)務(wù)入侵的。
Saga 模式下分布式事務(wù)通常是由事件驅(qū)動的,各個參與者之間是異步執(zhí)行的,Saga 模式是一種長事務(wù)解決方案。
Saga 模式使用場景:
- Saga 模式適用于業(yè)務(wù)流程長且需要保證事務(wù)終一致性的業(yè)務(wù)系統(tǒng),Saga 模式一階段就會提交本地事務(wù),無鎖、長流程情況下可以保證性能。
- 事務(wù)參與者可能是其它公司的服務(wù)或者是遺留系統(tǒng)的服務(wù),無法進行改造和提供 TCC 要求的接口,可以使用 Saga 模式。
Saga模式的優(yōu)勢:
- 一階段提交本地數(shù)據(jù)庫事務(wù),無鎖,高性能;
- 參與者可以采用事務(wù)驅(qū)動異步執(zhí)行,高吞吐;
- 補償服務(wù)即正向服務(wù)的“反向”,易于理解,易于實現(xiàn);
Saga模式的缺點:
- Saga 模式由于一階段已經(jīng)提交本地數(shù)據(jù)庫事務(wù),且沒有進行“預(yù)留”動作,所以不能保證隔離性。后續(xù)會講到對于缺乏隔離性的應(yīng)對措施。
與TCC實踐經(jīng)驗相同的是,Saga 模式中,每個事務(wù)參與者的沖正、逆向操作,需要支持:
- 空補償:逆向操作早于正向操作時;
- 防懸掛控制:空補償后要拒絕正向操作;
- 冪等。
??(4)XA模式
XA是X/Open DTP組織(X/Open DTP group)定義的兩階段提交協(xié)議,XA被許多數(shù)據(jù)庫(如
Oracle、DB2、SQL Server、MySQL)和中間件等工具(如CICS 和 Tuxedo)本地支持 。
X/Open DTP模型(1994)包括應(yīng)用程序(AP)、事務(wù)管理器(TM)、資源管理器(RM)。
XA接口函數(shù)由數(shù)據(jù)庫廠商提供。XA規(guī)范的基礎(chǔ)是兩階段提交協(xié)議2PC。
JTA(Java Transaction API) 是Java實現(xiàn)的XA規(guī)范的增強版 接口。
在XA模式下,需要有一個[全局]協(xié)調(diào)器,每一個數(shù)據(jù)庫事務(wù)完成后,進行第一階段預(yù)提交,并通知協(xié)
調(diào)器,把結(jié)果給協(xié)調(diào)器。協(xié)調(diào)器等所有分支事務(wù)操作完成、都預(yù)提交后,進行第二步;第二步:協(xié)調(diào)器通知每個數(shù)據(jù)庫進行逐個commit/rollback。其中,這個全局協(xié)調(diào)器就是XA模型中的TM角色,每個分支事務(wù)各自的數(shù)據(jù)庫就是RM。
MySQL 提供的XA實現(xiàn):https://dev.mysql.com/doc/refman/5.7/en/xa.html
XA模式下的開源框架有: atomikos,其開發(fā)公司也有商業(yè)版本。
XA模式缺點: 事務(wù)粒度大。高并發(fā)下,系統(tǒng)可用性低。因此很少使用。
??(5)(AT、TCC、Saga、XA)模式分析
四種分布式事務(wù)模式,分別在不同的時間被提出,每種模式都有它的適用場景:
- AT 模式是無侵入的分布式事務(wù)解決方案,適用于不希望對業(yè)務(wù)進行改造的場景,幾乎0學(xué) 習(xí)成本。
- TCC 模式是高性能分布式事務(wù)解決方案,適用于核心系統(tǒng)等對性能有很高要求的場景。
- Saga 模式是長事務(wù)解決方案,適用于業(yè)務(wù)流程長且需要保證事務(wù)最終一致性的業(yè)務(wù)系統(tǒng),Saga模式一階段就會提交本地事務(wù),無鎖,長流程情況下可以保證性能,多用于渠道層、集成 層業(yè)務(wù)系統(tǒng)。事務(wù)參與者可能是其它公司的服務(wù)或者是遺留系統(tǒng)的服務(wù),無法進行改造和提供TCC 要求的接口,也可以使用 Saga 模式。
- XA模式是分布式強一致性的解決方案,但性能低而使用較少。
總結(jié):
分布式事務(wù)本身就是一個技術(shù)難題,業(yè)務(wù)中具體使用哪種方案還是需要不同的業(yè)務(wù)特點自行選擇,但是我們也會發(fā)現(xiàn),分布式事務(wù)會大大的提高流程的復(fù)雜度,會帶來很多額外的開銷工作,代碼量上去了,業(yè)務(wù)復(fù)雜了,性能下跌了。
所以,當(dāng)我們真實開發(fā)的過程中,能不使用分布式事務(wù)就不使用。
二、分布式事務(wù)Seata使用
1??Seata是什么
Seata 是一款開源的分布式事務(wù)解決方案,致力于提供高性能和簡單易用的分布式事務(wù)服務(wù)。Seata 將為用戶提供 AT、TCC、SAGA 和XA 事務(wù)模式,為用戶打造一站式的分布式解決方案。AT模式是阿里首推的模式,阿里云上有商用版本的GTS(Global Transaction Service 全局事務(wù)服務(wù))
官網(wǎng): https://seata.io/zh-cn/index.html
源碼: https://github.com/seata/seata
官方Demo: https://github.com/seata/seata-samples
2??Seata的三大角色
在 Seata 的架構(gòu)中,一共有三個角色:
- TC (Transaction Coordinator) - 事務(wù)協(xié)調(diào)者: 維護全局和分支事務(wù)的狀態(tài),驅(qū)動全局事務(wù)提交或回滾。
- TM (Transaction Manager) - 事務(wù)管理器: 定義全局事務(wù)的范圍:開始全局事務(wù)、提交或回滾全局事務(wù)。
- RM (Resource Manager) - 資源管理器: 管理分支事務(wù)處理的資源,與TC交談以注冊分支事務(wù)和報告分支事務(wù)的狀態(tài),并驅(qū)動分支事務(wù)提交或回滾。
其中,TC 為單獨部署的 Server 服務(wù)端,TM 和 RM 為嵌入到應(yīng)用中的 Client 客戶端。
在 Seata 中,一個分布式事務(wù)的生命周期如下:
- (1)TM 請求 TC 開啟一個全局事務(wù)。TC 會生成一個 XID作為該全局事務(wù)的編號。XID,會在微服務(wù)的調(diào)用鏈路中傳播,保證將多個微服務(wù)的子事務(wù)關(guān)聯(lián)在一起。當(dāng)一進入事務(wù)方法中就會生成XID ,global_table 就是存儲的全局事務(wù)信息 ;
- (2)RM 請求 TC 將本地事務(wù)注冊為全局事務(wù)的分支事務(wù),通過全局事務(wù)的 XID 進行關(guān)聯(lián)。當(dāng)運行數(shù)據(jù)庫操作方法,branch_table 存儲事務(wù)參與者;
- (3)TM 請求 TC 告訴 XID 對應(yīng)的全局事務(wù)是進行提交還是回滾;
- (4)TC 驅(qū)動 RM 們將 XID 對應(yīng)的自己的本地事務(wù)進行提交還是回滾。
3??Seata的設(shè)計思路
AT模式的核心是對業(yè)務(wù)無侵入,是一種改進后的兩階段提交,其設(shè)計思路如圖。
第一階段業(yè)務(wù)數(shù)據(jù)和回滾日志記錄在同一個本地事務(wù)中提交,釋放本地鎖和連接資源。核心在于對業(yè)務(wù)sql進行解析,轉(zhuǎn)換成undolog,并同時入庫,這是怎么做的呢?
先拋出一個概念DataSourceProxy代理數(shù)據(jù)源,通過名字大家大概也能基本猜到是什么個操作,后面做具體分析參考官方文檔: https://seata.io/zh-cn/docs/dev/mode/at-mode.html
第二階段分布式事務(wù)操作成功,則TC通知RM異步刪除undolog。
分布式事務(wù)操作失敗,TM向TC發(fā)送回滾請求,RM 收到協(xié)調(diào)器TC發(fā)來的回滾請求,通過 XID 和 Branch ID 找到相應(yīng)的回滾日志記錄,通過回滾記錄生成反向的更新 SQL 并執(zhí)行,以完成分支的回滾。
整體執(zhí)行流程:
4??Seata的設(shè)計亮點
相比與其它分布式事務(wù)框架,Seata架構(gòu)的亮點主要有幾個:
- (1)應(yīng)用層基于SQL解析實現(xiàn)了自動補償,從而最大程度的降低業(yè)務(wù)侵入性;
- (2)將分布式事務(wù)中TC(事務(wù)協(xié)調(diào)者)獨立部署,負(fù)責(zé)事務(wù)的注冊、回滾;
- (3)通過全局鎖實現(xiàn)了寫隔離與讀隔離。
5??Seata存在的問題
- 性能損耗
一條Update的SQL,則需要全局事務(wù)xid獲?。ㄅcTC通訊)、before image(解析SQL,查詢一次數(shù)據(jù)庫)、after image(查詢一次數(shù)據(jù)庫)、insert undo log(寫一次數(shù)據(jù)庫)、before commit(與TC通訊,判斷鎖沖突),這些操作都需要一次遠(yuǎn)程通訊RPC,而且是同步的。另外undo log寫入時blob字段的插入性能也是不高的。每條寫SQL都會增加這么多開銷,粗略估計會增加5倍響應(yīng)時間。
- 性價比
為了進行自動補償,需要對所有交易生成前后鏡像并持久化,可是在實際業(yè)務(wù)場景下,這個是成功率有多高,或者說分布式事務(wù)失敗需要回滾的有多少比率?按照二八原則預(yù)估,為了20%的交易回滾,需要將80%的成功交易的響應(yīng)時間增加5倍,這樣的代價相比于讓應(yīng)用開發(fā)一個補償交易是否是值得?
- 全局鎖——熱點數(shù)據(jù)
相比XA,Seata 雖然在一階段成功后會釋放數(shù)據(jù)庫鎖,但一階段在commit前全局鎖的判定也拉長了對數(shù)據(jù)鎖的占有時間,這個開銷比XA的prepare低多少需要根據(jù)實際業(yè)務(wù)場景進行測試。全局鎖的引入實現(xiàn)了隔離性,但帶來的問題就是阻塞,降低并發(fā)性,尤其是熱點數(shù)據(jù),這個問題會更加嚴(yán)重。
- 全局鎖——回滾鎖釋放時間
Seata在回滾時,需要先刪除各節(jié)點的undo log,然后才能釋放TC內(nèi)存中的鎖,所以如果第二階段是回滾,釋放鎖的時間會更長。
- 死鎖問題
Seata的引入全局鎖會額外增加死鎖的風(fēng)險,但如果出現(xiàn)死鎖,會不斷進行重試,最后靠等待全局鎖超時,這種方式并不優(yōu)雅,也延長了對數(shù)據(jù)庫鎖的占有時間。
三、Seata快速開始
1??Seata Server(TC)環(huán)境搭建
Seata部署指南:https://seata.io/zh-cn/docs/ops/deploy-guide-beginner.html
??步驟一:下載安裝包
下載地址:https://github.com/seata/seata/releases
目前最新的版本為1.6.1:
??步驟二:存儲模式配置
啟動包: seata–>conf–>application.yml,修改store.mode=“db或者redis”
源碼: 根目錄–>seata-server–>resources–>application.yml,修改store.mode=“db或者redis”
1.5.0以下版本:
啟動包: seata–>conf–>file.conf,修改store.mode=“db或者redis”
源碼: 根目錄–>seata-server–>resources–>file.conf,修改store.mode=“db或者redis”
??步驟三:建表(僅db存儲模式)
新建表: 可以去seata提供的資源信息中下載:https://github.com/seata/seata/blob/1.6.1/script/server/db/mysql.sql
??步驟四:修改數(shù)據(jù)庫連接|redis屬性配置
啟動包: seata–>conf–>application.example.yml中附帶額外配置,將其db|redis相關(guān)配置復(fù)制至application.yml,進行修改store.db或store.redis相關(guān)屬性。
源碼: 根目錄–>seata-server–>resources–>application.example.yml中附帶額外配置,將其db|redis相關(guān)配置復(fù)制至application.yml,進行修改store.db或store.redis相關(guān)屬性。
1.5.0以下版本:
啟動包: seata–>conf–>file.conf,修改store.db或store.redis相關(guān)屬性。
源碼: 根目錄–>seata-server–>resources–>file.conf,修改store.db或store.redis相關(guān)屬性。
??步驟五:啟動
碼啟動: 執(zhí)行ServerApplication.java的main方法
命令啟動:
seata-server.sh -h 127.0.0.1 -p 8091 -m db
1.5.0以下版本
源碼啟動: 執(zhí)行Server.java的main方法
命令啟動:
seata-server.sh -h 127.0.0.1 -p 8091 -m db -n 1 -e test
命令啟動參數(shù):
-h: 注冊到注冊中心的ip
-p: Server rpc 監(jiān)聽端口
-m: 全局事務(wù)會話信息存儲模式,file、db、redis,優(yōu)先讀取啟動參數(shù) (Seata-Server 1.3及以上版本支持redis)
-n: Server node,多個Server時,需區(qū)分各自節(jié)點,用于生成不同區(qū)間的transactionId,以免沖突
-e: 多環(huán)境配置參考 http://seata.io/en-us/docs/ops/multi-configuration-isolation.html
2??業(yè)務(wù)系統(tǒng)集成Client
??步驟一:添加seata依賴(建議單選)
- 依賴seata-all
- 依賴seata-spring-boot-starter,支持yml、properties配置(.conf可刪除),內(nèi)部已依賴seata-all
- 依賴spring-cloud-alibaba-seata,內(nèi)部集成了seata,并實現(xiàn)了xid傳遞
??步驟二:undo_log建表、配置參數(shù)(僅AT模式)
??步驟三:數(shù)據(jù)源代理(不支持自動和手動配置并存)
(1)如果使用seata-all:
- 0.9.0版本開始seata支持自動代理數(shù)據(jù)源:
1.1.0: seata-all取消屬性配置,改由注解@EnableAutoDataSourceProxy開啟,并可選擇jdk proxy或者cglib proxy
1.0.0: client.support.spring.datasource.autoproxy=true
0.9.0: support.spring.datasource.autoproxy=true
如果采用XA模式,@EnableAutoDataSourceProxy(dataSourceProxyMode = "XA")
- 手動配置可參考下方的例子:
@Primary
@Bean("dataSource")
public DataSource dataSource(DataSource druidDataSource) {
//AT 代理 二選一
return new DataSourceProxy(druidDataSource);
//XA 代理
return new DataSourceProxyXA(druidDataSource)
}
(2)如果使用seata-starter
- 使用自動代理數(shù)據(jù)源時,如果使用XA模式還需要調(diào)整配置文件
application.yml:
seata:
data-source-proxy-mode: XA
- 如何關(guān)閉seata-spring-boot-starter的數(shù)據(jù)源自動代理?
application.yml:
seata:
enable-auto-data-source-proxy: false
??步驟四:初始化GlobalTransactionScanner
手動:
@Bean
public GlobalTransactionScanner globalTransactionScanner() {
String applicationName = this.applicationContext.getEnvironment().getProperty("spring.application.name");
String txServiceGroup = this.seataProperties.getTxServiceGroup();
if (StringUtils.isEmpty(txServiceGroup)) {
txServiceGroup = applicationName + "-fescar-service-group";
this.seataProperties.setTxServiceGroup(txServiceGroup);
}
return new GlobalTransactionScanner(applicationName, txServiceGroup);
}
自動:
引入seata-spring-boot-starter、spring-cloud-starter-alibaba-seata等jar
??步驟五:實現(xiàn)xid跨服務(wù)傳遞
- 手動 參考源碼integration文件夾下的各種rpc實現(xiàn) module
- 自動 springCloud用戶可以引入spring-cloud-starter-alibaba-seata,內(nèi)部已經(jīng)實現(xiàn)xid傳遞
??業(yè)務(wù)使用——注解攔截
全局事務(wù):
@GetMapping(value = "testCommit")
@GlobalTransactional
public Object testCommit(@RequestParam(name = "id",defaultValue = "1") Integer id,
@RequestParam(name = "sum", defaultValue = "1") Integer sum) {
Boolean ok = productService.reduceStock(id, sum);
if (ok) {
LocalDateTime now = LocalDateTime.now();
Orders orders = new Orders();
orders.setCreateTime(now);
orders.setProductId(id);
orders.setReplaceTime(now);
orders.setSum(sum);
orderService.save(orders);
return "ok";
} else {
return "fail";
}
}
TCC:
/**
* 定義兩階段提交 name = 該tcc的bean名稱,全局唯一 commitMethod = commit 為二階段確認(rèn)方法 rollbackMethod = rollback 為二階段取消方法
* useTCCFence=true 為開啟防懸掛
* BusinessActionContextParameter注解 傳遞參數(shù)到二階段中
*
* @param params -入?yún)? * @return String
*/
@TwoPhaseBusinessAction(name = "beanName", commitMethod = "commit", rollbackMethod = "rollback", useTCCFence = true)
public void insert(@BusinessActionContextParameter(paramName = "params") Map<String, String> params) {
logger.info("此處可以預(yù)留資源,或者利用tcc的特點,與AT混用,二階段時利用一階段在此處存放的消息,通過二階段發(fā)出,比如redis,mq等操作");
}
/**
* 確認(rèn)方法、可以另命名,但要保證與commitMethod一致 context可以傳遞try方法的參數(shù)
*
* @param context 上下文
* @return boolean
*/
public void commit(BusinessActionContext context) {
logger.info("預(yù)留資源真正處理,或者發(fā)出mq消息和redis入庫");
}
/**
* 二階段取消方法
*
* @param context 上下文
* @return boolean
*/
public void rollback(BusinessActionContext context) {
logger.info("預(yù)留資源釋放,或清除一階段準(zhǔn)備讓二階段提交時發(fā)出的消息緩存");
}
??業(yè)務(wù)使用——切點表達(dá)式
全局事務(wù):
@Bean
public AspectTransactionalInterceptor aspectTransactionalInterceptor () {
return new AspectTransactionalInterceptor();
}
@Bean
public Advisor txAdviceAdvisor(AspectTransactionalInterceptor aspectTransactionalInterceptor ) {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("配置切點表達(dá)式使全局事務(wù)攔截器生效");
return new DefaultPointcutAdvisor(pointcut, aspectTransactionalInterceptor);
}
后記
文章來源:http://www.zghlxwxcb.cn/news/detail-779534.html
??Java全棧學(xué)習(xí)路線可參考:【Java全棧學(xué)習(xí)路線】最全的Java學(xué)習(xí)路線及知識清單,Java自學(xué)方向指引,內(nèi)含最全Java全棧學(xué)習(xí)技術(shù)清單~
??算法刷題路線可參考:算法刷題路線總結(jié)與相關(guān)資料分享,內(nèi)含最詳盡的算法刷題路線指南及相關(guān)資料分享~文章來源地址http://www.zghlxwxcb.cn/news/detail-779534.html
到了這里,關(guān)于Spring Cloud Alibaba全家桶(九)——分布式事務(wù)組件Seata的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!