自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

深度剖析分布式事務(wù),輕松掌握實(shí)現(xiàn)原理與應(yīng)用技巧!

開發(fā) 前端
二階段 Confirm/Cancel 方法執(zhí)行后,將狀態(tài)改為 committed 或 rollbacked 狀態(tài)。當(dāng)重復(fù)調(diào)用二階段 Confirm/Cancel 方法時(shí),判斷事務(wù)狀態(tài)即可解決冪等問題。

前言

大家好,今天我們來一起探討分布式事務(wù)的相關(guān)知識(shí)。相信大家都有多多少少接觸過分布式事務(wù),因?yàn)槲覀儸F(xiàn)在寫的代碼可是服務(wù)于億級用戶量級的,那么大的請求量級不可能全部寫在一臺(tái)服務(wù)器上面對吧。如果你還沒有研究過分布式事務(wù),也沒關(guān)系,我們今天再一起來探討一番。我曾經(jīng)接觸過分布式事務(wù)相關(guān)的中間件框架,比如現(xiàn)在很火的阿里開源的一款分布式事務(wù)中間件Seata。目前我在Seata社區(qū)主要做一些RPC以及性能優(yōu)化的相關(guān)工作,所以我可能會(huì)對分布式事務(wù)具體實(shí)現(xiàn)比較了解。以Seata為契機(jī),我們一起來探討分布式事務(wù)。

什么是事務(wù)?

開始前,先來問大家兩個(gè)問題:

第一問題:什么是事務(wù)?

在編寫代碼的時(shí)候,我們常常會(huì)遇到各種事務(wù)問題。那么,我們該如何清晰明了地描述事務(wù)的概念呢?事務(wù)是指如何確保對一組(多個(gè))數(shù)據(jù)操作在執(zhí)行的過程中,要么全部都能夠成功執(zhí)行,要么全部失敗。而且,一旦事務(wù)成功執(zhí)行,所變更的數(shù)據(jù)不會(huì)丟失;若事務(wù)失敗,所有的數(shù)據(jù)變更都要回到事務(wù)開始之前的狀態(tài)。簡單來說,事務(wù)包括多個(gè)操作,這些操作要么全部執(zhí)行,要么全部不執(zhí)行。

第一問題:保證事務(wù)的目的是什么?

在理解事務(wù)的概念后,我們需要明確實(shí)現(xiàn)事務(wù)的最終目的是什么?如果在一組事務(wù)中,有些操作執(zhí)行了,有些沒執(zhí)行,會(huì)產(chǎn)生什么問題呢?舉個(gè)例子,如果你給父母轉(zhuǎn)賬1W元,結(jié)果你的賬戶扣了1W元,但是你父母的賬戶卻沒有加上1W元,這時(shí)你就會(huì)開始懷疑自己賺錢的意義。這種情況就是所謂的“數(shù)據(jù)一致性問題”。

當(dāng)大家明確了以上兩個(gè)問題之后,我才能繼續(xù)往下跟大家繼續(xù)分享今天的這個(gè)主題,因?yàn)榻裉爝@個(gè)主題,都是在圍繞著怎么保證事務(wù)一致性的問題展開的。

單進(jìn)程下完美的解決方案

圖片

A(原子性)、C(一致性)、I(隔離性)、D(持久性)。C 是事務(wù)最終的目標(biāo),那么A、I、D 就是為實(shí)現(xiàn)這個(gè)目標(biāo)努力的打工仔,如果這幾個(gè)打工仔不能正常工作的話,那么一致性就得不到保障。

原子性:原子性是指一組操作要么全部執(zhí)行成功,要么全部執(zhí)行失敗。這個(gè)概念和事務(wù)十分相似。如果不保證原子性,就可能出現(xiàn)在同一個(gè)事務(wù)中,某些操作執(zhí)行成功,而另一些操作執(zhí)行失敗的情況,這會(huì)導(dǎo)致數(shù)據(jù)不一致,而且很難恢復(fù)。因此,原子性是保障數(shù)據(jù)一致性的重要特性之一。

隔離性: 事務(wù)的隔離性指的是多個(gè)事務(wù)之間的操作不會(huì)相互影響,它們之間相互隔離。如果沒有隔離性,就好像兩個(gè)人在同一張畫布上畫畫,一個(gè)畫豬,一個(gè)畫狗,最后會(huì)畫出一個(gè)四不像。也就是說,如果不保證隔離性,一個(gè)人修改數(shù)據(jù)時(shí),其他人也可以修改,這會(huì)導(dǎo)致數(shù)據(jù)不一致。

持久性:持久性指的是一旦事務(wù)提交,所產(chǎn)生的數(shù)據(jù)變更不會(huì)因?yàn)槿魏我馔猓ū热鐢?shù)據(jù)庫故障或服務(wù)器宕機(jī))而丟失。因?yàn)槿绻聞?wù)產(chǎn)生的部分?jǐn)?shù)據(jù)丟失,就會(huì)導(dǎo)致數(shù)據(jù)不一致。

單機(jī)事務(wù)實(shí)現(xiàn)采用ACID模型,通過加鎖實(shí)現(xiàn)對需要操作相同數(shù)據(jù)的事務(wù)進(jìn)行隔離,保證事務(wù)之間的操作不會(huì)相互影響,從而實(shí)現(xiàn)了隔離性。在事務(wù)提交之前,記錄數(shù)據(jù)修改前的日志(undo log)和事務(wù)需要變更數(shù)據(jù)的日志(redo log),以保證事務(wù)不論在哪個(gè)階段都能通過undo log對事務(wù)數(shù)據(jù)進(jìn)行回滾,把數(shù)據(jù)恢復(fù)到事務(wù)開始之前的狀態(tài)。同時(shí),通過redo log保證事務(wù)在提交后,即使數(shù)據(jù)庫或服務(wù)器出現(xiàn)故障,也能重做未成功寫入磁盤的數(shù)據(jù),實(shí)現(xiàn)了事務(wù)的持久性和原子性。

分布式事務(wù)的誕生

圖片

在公司發(fā)展初期,由于用戶量少、數(shù)據(jù)量少,系統(tǒng)的并發(fā)請求并不高,因此只需要將應(yīng)用單點(diǎn)部署即可滿足業(yè)務(wù)需求。但隨著業(yè)務(wù)的快速發(fā)展和復(fù)雜度的增加,幾乎每個(gè)公司的系統(tǒng)都會(huì)從單體架構(gòu)轉(zhuǎn)向分布式架構(gòu),特別是微服務(wù)架構(gòu)。

單進(jìn)程事務(wù)演變成多進(jìn)程事務(wù)時(shí),場景發(fā)生了改變。之前是一個(gè)人獨(dú)立完成一項(xiàng)任務(wù),現(xiàn)在變成了多個(gè)人協(xié)作完成同一項(xiàng)任務(wù)。在單進(jìn)程事務(wù)中,決定權(quán)在自己手中,因此決定回滾或提交事務(wù)較為容易。但在多進(jìn)程事務(wù)中,如何協(xié)調(diào)多個(gè)人的操作以達(dá)到一致性,則成為一個(gè)難題。因此,需要有一個(gè)統(tǒng)一的協(xié)調(diào)者來協(xié)調(diào)多個(gè)節(jié)點(diǎn)的操作,以確保多個(gè)進(jìn)程操作的一致性。

圖片

比如從圖中看到,假設(shè)在RPC調(diào)用過程中,其中有一個(gè)RPC調(diào)用異常了,我們怎么去回滾前面兩個(gè)已經(jīng)執(zhí)行成功的事務(wù)呢?

這就不得不涉及到我們應(yīng)該怎么去設(shè)計(jì)一個(gè)分布式事務(wù)的執(zhí)行模型。

分布式事務(wù)模型:2PC

目前絕大部分分布式事務(wù)框架為 2PC 二階段事務(wù)模型。

圖片

2PC協(xié)議的核心思路是協(xié)調(diào)者和參與者通過兩個(gè)階段的協(xié)商達(dá)成最終操作的一致性。首先,第一階段的目的是確認(rèn)各個(gè)參與者是否具備執(zhí)行事務(wù)的條件。根據(jù)第一階段參與者的響應(yīng)結(jié)果,制定出第二階段的事務(wù)策略。如果第一階段中任意一個(gè)參與者不具備事務(wù)執(zhí)行條件,那么第二階段的決策就是回滾事務(wù)。只有在所有參與者都具備事務(wù)執(zhí)行條件的情況下,才進(jìn)行整體事務(wù)的提交。

但是這個(gè)模型也不是萬能的,在遇到異常情況,很可能就會(huì)造成數(shù)據(jù)不一致(但是這個(gè)不一致,在最后都會(huì)有框架驅(qū)動(dòng)達(dá)成最終一致性)

我下面舉兩個(gè)例子

參與者掛掉

如果在第一階段,協(xié)調(diào)者發(fā)送Prepare指令給所有的參與者后,參與者掛掉了,那么此時(shí)協(xié)調(diào)者因?yàn)檫t遲收不到參與者的消息而導(dǎo)致超時(shí),所以協(xié)調(diào)者在超時(shí)之后會(huì)統(tǒng)一發(fā)送abort指令進(jìn)行事務(wù)回滾。

如果在第二階段,協(xié)調(diào)者發(fā)送commit或者abort指令給所有參與者后,參與者掛掉了,那么協(xié)調(diào)者會(huì)在超時(shí)之后進(jìn)行消息重發(fā),直到參與者恢復(fù)后收到到commit或者abort ,向協(xié)調(diào)者返回成功。

協(xié)調(diào)者掛掉

協(xié)調(diào)者在第一階段發(fā)送Prepare指令后掛掉,那么此時(shí)參與者此時(shí)會(huì)一直得不到協(xié)調(diào)者下一步的指令,那么此時(shí)參與者會(huì)一直陷入阻塞狀態(tài),資源也會(huì)一直被鎖住,直到協(xié)調(diào)者恢復(fù)之后向參與者發(fā)出下一步的指令。

協(xié)調(diào)者在第二階段掛掉,那么此時(shí)協(xié)調(diào)者已向所有者發(fā)出最后階段的指令了,所以收到指令的參與者會(huì)完成最后的commit或rollback操作,對于參與者來說事務(wù)已經(jīng)結(jié)束,所以不存在阻塞和鎖的問題, 當(dāng)協(xié)調(diào)者恢復(fù)后,會(huì)把事務(wù)日志狀態(tài)標(biāo)記為結(jié)束。

CAP 定律

圖片

強(qiáng)一致性的事務(wù)一致性方案在單機(jī)事務(wù)場景下可以完美實(shí)現(xiàn),但在分布式事務(wù)場景下效果并不理想。這是因?yàn)閱螜C(jī)事務(wù)和分布式事務(wù)所面臨的場景不同。在單機(jī)事務(wù)中,只需要考慮數(shù)據(jù)一致性問題。而在分布式事務(wù)場景中,需要同時(shí)考慮數(shù)據(jù)一致性、多節(jié)點(diǎn)的可用性、網(wǎng)絡(luò)分區(qū)等多個(gè)問題。因此,強(qiáng)一致性的事務(wù)模型始終無法完美解決分布式事務(wù)場景。

由此引出CAP定律,什么是CAP定律呢?

CA組合就是保證一致性和可用性,放棄分區(qū)容忍性,即不進(jìn)行分區(qū),不考慮由于網(wǎng)絡(luò)不通或節(jié)點(diǎn)掛掉的問題。那么系統(tǒng)將不是一個(gè)標(biāo)準(zhǔn)的分布式系統(tǒng),我們最常用的關(guān)系型數(shù)據(jù)庫就滿足了CA。

CP組合就是保證一致性和分區(qū)容忍性,放棄可用性。Zookerper就是追求強(qiáng)一致性,放棄了可用性,還有跨行轉(zhuǎn)賬,一次轉(zhuǎn)賬請求要等待雙方銀行系統(tǒng)都完成整個(gè)事務(wù)才能完成。

AP組合就是保證可用性和分區(qū)容忍性,放棄一致性。這是分布式系統(tǒng)設(shè)計(jì)時(shí)的選擇。

BASE 理論

圖片

CAP理論表明在分布式系統(tǒng)中,無法同時(shí)滿足一致性(Consistency)、可用性(Availability)和分區(qū)容錯(cuò)性(Partition tolerance)。在分布式系統(tǒng)中,分區(qū)容錯(cuò)性是必須滿足的,而可用性是分布式系統(tǒng)設(shè)計(jì)的主要目標(biāo),通常需要犧牲一致性來保證可用性和分區(qū)容錯(cuò)性。

但是,犧牲一致性并不意味著完全放棄它。所謂犧牲,是指在一段時(shí)間內(nèi),系統(tǒng)可以暫時(shí)不保證一致性,但最終還是要恢復(fù)到一致性狀態(tài),通常被稱為最終一致性?;谧罱K一致性模型,BASE理論提出了一套實(shí)踐理論,從基本可用性、軟狀態(tài)和最終一致性三個(gè)方面來指導(dǎo)我們進(jìn)行分布式系統(tǒng)設(shè)計(jì)。

Seata介紹

Seata(Simple Extensible Autonomous Transaction Architecture,簡單可擴(kuò)展自治事務(wù)框架)是 2019 年 1 月份阿里巴巴和螞蟻集團(tuán)共同開源的分布式事務(wù)解決方案。目前在GitHub已經(jīng)有超過 2 萬+ star,社區(qū)非?;钴S。我在19年7月份的時(shí)候正式加入Seata開源社區(qū)。

在整個(gè) Seata 體系下,所有模式(AT、TCC、XA、SAGA)都遵循這套角色模型。

Seata AT 模式

從圖中以及代碼中可以看到,在分布式事務(wù)場景下,只需要在發(fā)起方的方法上面添加注解@GlobalTransaction注解就可以了,完全不「干擾」業(yè)務(wù)的邏輯。

Seata AT 模式:通信交互

可以看出,AT 模式遵循 TC、TM、RM 交互:

  1. 首先TM回向TC服務(wù)發(fā)送一個(gè)Begin指令開啟全局事務(wù),TC 返回全局事務(wù)xid;
  2. 各個(gè)分支事務(wù)向TC服務(wù)發(fā)送Branch Register進(jìn)行分支事務(wù)注冊;
  3. TM 向TC服務(wù)決議全局提交或者回滾,TC 收到TM最終的二階段指令后,會(huì)驅(qū)動(dòng)各個(gè)分支進(jìn)行提交或者回滾。

可以看出,Seata AT模式是一個(gè) 2PC 事務(wù)模型。

Seata AT 模式如何保證對業(yè)務(wù)的無入侵?

1、數(shù)據(jù)源代理

Seata 在數(shù)據(jù)源做了一層代理層,所以我們使用 Seata 時(shí),我們使用的數(shù)據(jù)源實(shí)際上用的是 Seata 自帶的數(shù)據(jù)源代理 DataSourceProxy,Seata 在這層代理中加入了很多邏輯,主要是解析 SQL,把業(yè)務(wù)數(shù)據(jù)在更新前后的數(shù)據(jù)鏡像組織成回滾日志,并將 undo log 日志插入 undo_log 表中,保證每條更新數(shù)據(jù)的業(yè)務(wù) sql 都有對應(yīng)的回滾日志存在。

2、一階段

可以看出,AT模式的分支事務(wù),必須使用支持ACID的關(guān)系型數(shù)據(jù)且業(yè)務(wù)與回滾日志需要在同一個(gè)數(shù)據(jù)庫中,因?yàn)闃I(yè)務(wù)SQL和回滾日志,需要使用本地事務(wù)同時(shí)插入數(shù)據(jù)庫中,要么同時(shí)成功要么同時(shí)失敗。如果分開在不同數(shù)據(jù)庫中,就又會(huì)產(chǎn)生分布式事務(wù)問題,這純屬于套娃行為了。

3、二階段提交

當(dāng)TM決議全局事務(wù)提交,TC會(huì)發(fā)送commit指令給各個(gè)分支事務(wù),因?yàn)椤皹I(yè)務(wù) SQL”在一階段已經(jīng)提交至數(shù)據(jù)庫, 所以 Seata 框架只需將一階段保存的快照數(shù)據(jù)和行鎖刪掉,完成數(shù)據(jù)清理即可。

4、二階段回滾

當(dāng)TM決議全局事務(wù)回滾,TC會(huì)發(fā)送rollback指令給各個(gè)分支事務(wù),回滾方式便是用“before image”還原業(yè)務(wù)數(shù)據(jù);但在還原前要首先要校驗(yàn)臟寫,對比“數(shù)據(jù)庫當(dāng)前業(yè)務(wù)數(shù)據(jù)”和 “after image”,如果兩份數(shù)據(jù)完全一致就說明沒有臟寫,可以還原業(yè)務(wù)數(shù)據(jù),如果不一致就說明有臟寫,出現(xiàn)臟寫就需要轉(zhuǎn)人工處理。

從整個(gè)流程可以看出來,在沒有發(fā)生臟寫的情況下,所有的事務(wù)操作都被Seata數(shù)據(jù)源代理悄悄地處理了。

Seata AT 模式:事務(wù)隔離級別

剛剛我們說到臟寫,那么Seata AT模式是怎么發(fā)生臟寫或者臟讀的呢?這不得不從Seata的默認(rèn)的事務(wù)隔離級別說起。

想象一個(gè)場景:

某個(gè)全局事務(wù)事務(wù)下有若干個(gè)分支事務(wù),在全局事務(wù)執(zhí)行過程中(全局事務(wù)還沒執(zhí)行完),某個(gè)本地事務(wù)提交了,如果Seata沒有采取任何措施,會(huì)造成什么問題?

傳統(tǒng)意義的臟讀是讀到了未提交的數(shù)據(jù),Seata 臟讀是讀到了全局事務(wù)下未提交的數(shù)據(jù),全局事務(wù)可能包含多個(gè)本地事務(wù),某個(gè)本地事務(wù)提交了不代表全局事務(wù)提交了。

在絕大部分應(yīng)用在讀已提交的隔離級別下工作是沒有問題的,而實(shí)際上,這當(dāng)中又有絕大多數(shù)的應(yīng)用場景,實(shí)際上工作在讀未提交的隔離級別下同樣沒有問題。

在極端場景下,應(yīng)用如果需要達(dá)到全局的讀已提交,Seata 設(shè)計(jì)了由事務(wù)協(xié)調(diào)器維護(hù)的全局寫排他鎖,來保證事務(wù)間的寫隔離,同時(shí),將全局事務(wù)默認(rèn)定義在讀未提交的隔離級別上。

但是默認(rèn)情況下,Seata 的全局事務(wù)是工作在讀未提交隔離級別的,保證絕大多數(shù)場景的高效性。

Seata AT 模式:寫隔離

1、提交成功

兩個(gè)全局事務(wù) tx1 和 tx2,分別對 a 表的 m 字段進(jìn)行更新操作,m 的初始值 1000。

tx1 先開始,開啟本地事務(wù),拿到本地鎖,更新操作 m = 1000 - 100 = 900。本地事務(wù)提交前,先拿到該記錄的 全局鎖 ,本地提交釋放本地鎖。tx2 后開始,開啟本地事務(wù),拿到本地鎖,更新操作 m = 900 - 100 = 800。本地事務(wù)提交前,嘗試拿該記錄的 全局鎖 ,tx1 全局提交前,該記錄的全局鎖被 tx1 持有,tx2 需要重試等待 全局鎖 。

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

2、事務(wù)回滾

如果 tx1 的二階段全局回滾,則 tx1 需要重新獲取該數(shù)據(jù)的本地鎖,進(jìn)行反向補(bǔ)償?shù)母虏僮?,?shí)現(xiàn)分支的回滾。

此時(shí),如果 tx2 仍在等待該數(shù)據(jù)的 全局鎖,同時(shí)持有本地鎖,則 tx1 的分支回滾會(huì)失敗。分支的回滾會(huì)一直重試,直到 tx2 的 全局鎖 等鎖超時(shí),放棄 全局鎖 并回滾本地事務(wù)釋放本地鎖,tx1 的分支回滾最終成功。

因?yàn)檎麄€(gè)過程 全局鎖 在 tx1 結(jié)束前一直是被 tx1 持有的,所以不會(huì)發(fā)生 臟寫 的問題。

Seata AT 模式:讀隔離

Seata AT模式下的臟讀是指在全局事務(wù)未提交之前,其他業(yè)務(wù)可能會(huì)讀取已提交的分支事務(wù)的數(shù)據(jù)。本質(zhì)上,這意味著Seata默認(rèn)的全局事務(wù)是讀未提交。

在特定場景下,可能需要全局讀取已提交數(shù)據(jù)。目前,Seata將通過代理SELECT FOR UPDATE語句來實(shí)現(xiàn)此需求。

執(zhí)行SELECT FOR UPDATE語句將申請全局鎖。如果全局鎖已被其他事務(wù)持有,則Seata將釋放本地鎖并回滾SELECT FOR UPDATE語句的本地執(zhí)行,并進(jìn)行重試。在此過程中,查詢將被阻塞,直到全局鎖被獲取,并確保讀取的數(shù)據(jù)是已提交的,然后才會(huì)返回查詢結(jié)果。

Seata AT 模式:與XA的區(qū)別

seata 的事務(wù)提交方式跟 XA 協(xié)議的兩段式提交在總體上來說基本是一致的,那它們之間有什么不同呢?

我們都知道 XA 協(xié)議它依賴的是數(shù)據(jù)庫層面來保障事務(wù)的一致性,也即是說 XA 的各個(gè)分支事務(wù)是在數(shù)據(jù)庫層面上驅(qū)動(dòng)的,由于 XA 的各個(gè)分支事務(wù)需要有 XA 的驅(qū)動(dòng)程序,一方面會(huì)導(dǎo)致數(shù)據(jù)庫與 XA 驅(qū)動(dòng)耦合,另一方面它會(huì)導(dǎo)致各個(gè)分支的事務(wù)資源鎖定周期長,這也是它沒有在互聯(lián)網(wǎng)公司流行的重要因素。

前面在將為什么無侵入的時(shí)候講到,Seata 在數(shù)據(jù)源做了一層代理層,所以我們使用 Seata 時(shí),我們使用的數(shù)據(jù)源實(shí)際上用的是 Seata 自帶的數(shù)據(jù)源代理 DataSourceProxy。

這樣做的好處就是,本地事務(wù)執(zhí)行完可以立即釋放本地事務(wù)鎖定的資源,然后向 TC 上報(bào)分支狀態(tài)。

當(dāng) TM 決議全局提交時(shí),就不需要同步協(xié)調(diào)處理了,TC 會(huì)異步調(diào)度各個(gè) RM 分支事務(wù)刪除對應(yīng)的 undo log 日志即可,這個(gè)步驟非??焖俚乜梢酝瓿?,XA就做不到,它必須同步等待所有分支處理完之后才認(rèn)為全局事務(wù)已完成,這個(gè)期間被鎖定的資源其它業(yè)務(wù)是不能訪問的,這也就是為什么XA性能這么差的原因。正常的業(yè)務(wù)來說,二階段commit的幾率遠(yuǎn)大于rollback,因此Seata AT模式相對于XA性能提升是非常巨大的。

當(dāng) TM 決議全局回滾時(shí),RM 收到 TC 發(fā)送的回滾請求,RM 通過 XID 找到對應(yīng)的 undo log 回滾日志,然后執(zhí)行回滾日志完成回滾操作。

如上圖所示,Seata 的 RM 實(shí)際上是已中間件的形式放在應(yīng)用層,不用依賴數(shù)據(jù)庫對協(xié)議的支持,完全剝離了分布式事務(wù)方案對數(shù)據(jù)庫在協(xié)議支持上的要求。

TCC 模式

TCC是分布式事務(wù)的一種解決方案,它也是一種2PC模型。

TCC優(yōu)點(diǎn):

1、性能高:沒有全局鎖,本地事務(wù)鎖在本地操作完成后馬上會(huì)釋放,不會(huì)像2PC、3PC 一樣整個(gè)事務(wù)執(zhí)行的過程都會(huì)鎖住資源,所以TCC性能非常高。

2、具備隔離性: 通過隔離資源達(dá)到事務(wù)隔離的目的,先預(yù)留資源,再真正使用資源,避免了出現(xiàn)兩個(gè)事務(wù)并發(fā)時(shí)可能導(dǎo)致的同一個(gè)資源被使用多次的問題,適合資源敏感的場景。

3、允許事務(wù)失?。嚎梢赃M(jìn)行事務(wù)回滾。

TCC缺點(diǎn):

1、業(yè)務(wù)侵入性強(qiáng):需要修改原來的結(jié)構(gòu)設(shè)計(jì)來預(yù)留資源, 需要在原有的方法基礎(chǔ)上把業(yè)務(wù)拆分為Try、Confirm、Cancel三個(gè)方法。

TCC適用場景:

有資源隔離性要求、并且對業(yè)務(wù)系統(tǒng)有控制權(quán),有修改結(jié)構(gòu)的權(quán)限。

Seata TCC 模式:使用效果

如圖所示,參與者需要實(shí)現(xiàn)Try、Confirm、Cancel這三個(gè)方法,并在Try方法中添加@TwoPhaseBusinessAction注解,填寫二階段commit和rollback的方式到注解參數(shù)中。隨后,使用Dubbo等rpc協(xié)議發(fā)布遠(yuǎn)程RPC服務(wù),在發(fā)起方的方法中添加@GlobalTransactional注解來開啟全局事務(wù),然后在全局事務(wù)內(nèi)調(diào)用參與者的一階段Try方法。此時(shí),二階段就由Seata框架來驅(qū)動(dòng)完成。

Seata TCC 模式:通信交互

結(jié)合剛剛的使用例子,我們來看看 Seata 是如何實(shí)現(xiàn)TCC模式的,在這張通信交互圖可以看出,它與AT模式一樣遵循 TC、TM、RM 角色模型。

其中TM負(fù)責(zé)開啟全局事務(wù),參與者執(zhí)行try方法時(shí)會(huì)注冊分支事務(wù),TM決議全局事務(wù)提交或回滾后,TC協(xié)調(diào)者會(huì)驅(qū)動(dòng)全局事務(wù)內(nèi)的參與者進(jìn)行提交或者回滾。

Seata TCC 模式:實(shí)踐例子

如圖所示,Try 方法作為一階段準(zhǔn)備方法,需要做資源的檢查和預(yù)留。在扣錢場景下,Try 要做的事情是就是檢查賬戶余額是否充足,預(yù)留轉(zhuǎn)賬資金,預(yù)留的方式就是凍結(jié) A 賬戶的 轉(zhuǎn)賬資金。Try 方法執(zhí)行之后,賬號 A 余額雖然還是 100,但是其中 30 元已經(jīng)被凍結(jié)了,不能被其他事務(wù)使用。

二階段 Confirm 方法執(zhí)行真正的扣錢操作。Confirm 會(huì)使用 Try 階段凍結(jié)的資金,執(zhí)行賬號扣款。Confirm 方法執(zhí)行之后,賬號 A 在一階段中凍結(jié)的 30 元已經(jīng)被扣除,賬號 A 余額變成 70 元 。

如果二階段是回滾的話,就需要在 Cancel 方法內(nèi)釋放一階段 Try 凍結(jié)的 30 元,使賬號 A 的回到初始狀態(tài),100 元全部可用。

用戶接入 TCC 模式,最重要的事情就是考慮如何將業(yè)務(wù)模型拆成 2 階段,實(shí)現(xiàn)成 TCC 的 3 個(gè)方法,并且保證 Try 成功 Confirm 一定能成功。相對于 AT 模式,TCC 模式對業(yè)務(wù)代碼有一定的侵入性,但是 TCC 模式無 AT 模式的全局行鎖,TCC 性能會(huì)比 AT 模式高很多。

TCC可能會(huì)遇到什么樣的問題?

即使我們擁有了一套完備的TCC接口,也不能高枕無憂。在微服務(wù)架構(gòu)下,很可能會(huì)遇到網(wǎng)絡(luò)超時(shí)、重發(fā)、機(jī)器宕機(jī)等一系列異常情況,這會(huì)導(dǎo)致分布式事務(wù)執(zhí)行出現(xiàn)異常。根據(jù)螞蟻多年的實(shí)踐,我們發(fā)現(xiàn)最常見的異常有三種,分別是空回滾、冪等、懸掛。

因此,TCC接口需要解決這三類問題。實(shí)際上,Seata框架已經(jīng)支持這三種異常的處理,我們將把這些異常的處理移植到Seata框架中。這樣,業(yè)務(wù)就無需關(guān)注這些異常情況,可以專注于業(yè)務(wù)邏輯。

雖然業(yè)務(wù)無需關(guān)注這些異常,但了解其內(nèi)部實(shí)現(xiàn)機(jī)制有助于更好地排查問題。接下來,我將為大家一一講解這三類異常出現(xiàn)的原因以及對應(yīng)的解決方案。

Seata TCC 模式:如何防止空回滾?

什么是空回滾?

TCC 服務(wù)在未收到 Try 請求的情況下收到 Cancel 請求,這種場景被稱為空回滾;空回滾在生產(chǎn)環(huán)境經(jīng)常出現(xiàn),用戶在實(shí)現(xiàn)TCC服務(wù)時(shí),應(yīng)允許允許空回滾的執(zhí)行,即收到空回滾時(shí)返回成功。

如圖所示,事務(wù)協(xié)調(diào)器在調(diào)用 TCC 服務(wù)的一階段 Try 操作時(shí),可能會(huì)出現(xiàn)因?yàn)閬G包而導(dǎo)致的網(wǎng)絡(luò)超時(shí),此時(shí)事務(wù)管理器會(huì)觸發(fā)二階段回滾,調(diào)用 TCC 服務(wù)的 Cancel 操作,而 Cancel 操作調(diào)用未出現(xiàn)超時(shí)。

要想防止空回滾,那么必須在 Cancel 方法中識(shí)別這是一個(gè)空回滾,Seata 是如何做的呢?

Seata 的做法是新增一個(gè) TCC 事務(wù)控制表,包含事務(wù)的 XID 和 BranchID 信息,在 Try 方法執(zhí)行時(shí)插入一條記錄,表示一階段執(zhí)行了,執(zhí)行 Cancel 方法時(shí)讀取這條記錄,如果記錄不存在,說明 Try 方法沒有執(zhí)行。

Seata TCC 模式:如何防懸掛?

懸掛指的是二階段 Cancel 方法比 一階段 Try 方法優(yōu)先執(zhí)行,由于允許空回滾的原因,在執(zhí)行完二階段 Cancel 方法之后直接空回滾返回成功,此時(shí)全局事務(wù)已結(jié)束,但是由于 Try 方法隨后執(zhí)行,這就會(huì)造成一階段 Try 方法預(yù)留的資源永遠(yuǎn)無法提交和釋放了。

那么懸掛是如何產(chǎn)生的呢?

在圖示中,當(dāng)事務(wù)協(xié)調(diào)器調(diào)用TCC服務(wù)的一階段Try操作時(shí),由于網(wǎng)絡(luò)擁堵等原因,可能會(huì)出現(xiàn)超時(shí)的情況。此時(shí),事務(wù)管理器會(huì)觸發(fā)二階段回滾,調(diào)用TCC服務(wù)的Cancel操作,但Cancel調(diào)用未超時(shí)。之后,被網(wǎng)絡(luò)擁堵延遲的一階段Try數(shù)據(jù)包被TCC服務(wù)收到,導(dǎo)致二階段Cancel請求比一階段Try請求先執(zhí)行,這會(huì)導(dǎo)致TCC服務(wù)在執(zhí)行晚到的Try之后,永遠(yuǎn)不會(huì)再收到二階段的Confirm或Cancel請求,從而導(dǎo)致TCC服務(wù)懸掛的情況。

用戶在實(shí)現(xiàn) TCC 服務(wù)時(shí),要允許空回滾,但是要拒絕執(zhí)行空回滾之后 Try 請求,要避免出現(xiàn)懸掛。

Seata 是怎么處理懸掛的呢?

在 TCC 事務(wù)控制表記錄狀態(tài)的字段 status 中增加一個(gè)狀態(tài):

  1. suspended:4

當(dāng)執(zhí)行二階段 Cancel 方法時(shí),如果發(fā)現(xiàn) TCC 事務(wù)控制表有相關(guān)記錄,說明二階段 Cancel 方法優(yōu)先一階段 Try 方法執(zhí)行,因此插入一條 status=4 狀態(tài)的記錄,當(dāng)一階段 Try 方法后面執(zhí)行時(shí),判斷 status=4 ,則說明有二階段 Cancel 已執(zhí)行,并返回 false 以阻止一階段 Try 方法執(zhí)行成功。

Seata TCC 模式:如何冪等控制?

冪等問題指的是 TC 重復(fù)進(jìn)行二階段提交,因此 Confirm/Cancel 接口需要支持冪等處理,即不會(huì)產(chǎn)生資源重復(fù)提交或者重復(fù)釋放。

那么冪等問題是如何產(chǎn)生的呢?

圖片

參與者執(zhí)行完二階段之后,由于網(wǎng)絡(luò)抖動(dòng)或者宕機(jī)問題,會(huì)造成 TC 收不到參與者執(zhí)行二階段的返回結(jié)果,TC 會(huì)重復(fù)發(fā)起調(diào)用,直到二階段執(zhí)行結(jié)果成功。

Seata 是如何處理冪等問題的呢?

同樣的也是在 TCC 事務(wù)控制表中增加一個(gè)記錄狀態(tài)的字段 status,該字段有有 3 個(gè)值,分別為:

  1. tried:1
  2. committed:2
  3. rollbacked:3

二階段 Confirm/Cancel 方法執(zhí)行后,將狀態(tài)改為 committed 或 rollbacked 狀態(tài)。當(dāng)重復(fù)調(diào)用二階段 Confirm/Cancel 方法時(shí),判斷事務(wù)狀態(tài)即可解決冪等問題。

責(zé)任編輯:武曉燕 來源: 后端進(jìn)階
相關(guān)推薦

2025-03-25 10:29:52

2022-06-21 08:27:22

Seata分布式事務(wù)

2024-06-28 09:07:19

2024-01-26 13:17:00

rollbackMQ訂單系統(tǒng)

2025-04-11 09:57:16

2022-06-27 08:21:05

Seata分布式事務(wù)微服務(wù)

2019-11-19 08:32:26

數(shù)據(jù)庫HLC事務(wù)

2024-06-07 08:06:36

2022-07-10 20:24:48

Seata分布式事務(wù)

2021-07-26 11:09:46

Redis分布式技術(shù)

2019-08-19 10:24:33

分布式事務(wù)數(shù)據(jù)庫

2024-09-12 14:50:08

2025-01-15 08:34:00

分布式事務(wù)服務(wù)

2023-09-14 15:44:46

分布式事務(wù)數(shù)據(jù)存儲(chǔ)

2021-08-06 08:33:27

Springboot分布式Seata

2017-07-26 15:08:05

大數(shù)據(jù)分布式事務(wù)

2020-03-31 08:05:23

分布式開發(fā)技術(shù)

2024-01-05 07:28:50

分布式事務(wù)框架

2024-11-28 15:11:28

2024-06-11 13:50:43

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號