基于Seata探尋分布式事務(wù)的實現(xiàn)方案
作者:京東物流技術(shù)與數(shù)據(jù)智能部 張碩
1、背景知識
隨著業(yè)務(wù)的快速發(fā)展、業(yè)務(wù)復(fù)雜度越來越高,幾乎每個公司的系統(tǒng)都會從單體走向分布式,特別是轉(zhuǎn)向微服務(wù)架構(gòu)。隨之而來就必然遇到分布式事務(wù)這個難題,這篇文章通過seata框架總結(jié)了分布式事務(wù)的幾種解決方案
1.1 ACID
關(guān)系型數(shù)據(jù)庫具有解決復(fù)雜事務(wù)場景的能力,關(guān)系型數(shù)據(jù)庫的事務(wù)滿足 ACID 的特性。
- Atomicity:原子性(要么都做,要么都不做)
- Consistency:一致性(數(shù)據(jù)庫只有一個狀態(tài),不存在未確定狀態(tài))
- Isolation:隔離性(事務(wù)之間互不干擾)
- Durability:永久性(事務(wù)一旦提交,數(shù)據(jù)庫記錄永久不變)
1.2 CAP
CAP 是指在一個分布式系統(tǒng)下, 包含三個要素:Consistency(一致性)、Availability(可用性)、Partition tolerance(分區(qū)容錯性),并且三者不可得兼。
- C:Consistency,一致性,所有數(shù)據(jù)變動都是同步的。
- A:Availability,可用性,即在可以接受的時間范圍內(nèi)正確地響應(yīng)用戶請求。
- P:Partition tolerance,分區(qū)容錯性,即某節(jié)點或網(wǎng)絡(luò)分區(qū)故障時,系統(tǒng)仍能夠提供滿足一致性和可用性的服務(wù)。
1.3 BASE
BASE 理論主要是解決 CAP 理論中分布式系統(tǒng)的可用性和一致性不可兼得的問題。BASE 理論包含以下三個要素:
- BA:Basically Available,基本可用。
- S:Soft State,軟狀態(tài),狀態(tài)可以有一段時間不同步。
- E:Eventually Consistent,最終一致,最終數(shù)據(jù)是一致的就可以了,而不是時時保持強(qiáng)一致。
2 實現(xiàn)模式
2.1 二段提交
第一階段(準(zhǔn)備階段)
TM 通知所有參與事務(wù)的各個 RM,給每個 RM 發(fā)送 prepare 消息。
RM 接收到消息后進(jìn)入準(zhǔn)備階段后,要么直接返回失敗,要么創(chuàng)建并執(zhí)行本地事務(wù),寫本地事務(wù)日志(redo 和 undo 日志),但是不提交(此處只保留最后一步耗時最少的提交操作給第二階段執(zhí)行)。
第二階段(提交 / 回滾階段)
Seata框架
基于兩階段提交模式,從設(shè)計上我們可以將整體分成三個大模塊,即TM、RM、TC,具體解釋如下:
- TM(Transaction Manager):全局事務(wù)管理器,控制全局事務(wù)邊界,負(fù)責(zé)全局事務(wù)開啟、全局提交、全局回滾。
- RM(Resource Manager):資源管理器,控制分支事務(wù),負(fù)責(zé)分支注冊、狀態(tài)匯報,并接收事務(wù)協(xié)調(diào)器的指令,驅(qū)動分支(本地)事務(wù)的提交和回滾。
- TC(Transaction Coordinator):事務(wù)協(xié)調(diào)器,維護(hù)全局事務(wù)的運行狀態(tài),負(fù)責(zé)協(xié)調(diào)并驅(qū)動全局事務(wù)的提交或回滾。
一個典型的分布式事務(wù)過程:
- TM 向 TC 申請開啟一個全局事務(wù),全局事務(wù)創(chuàng)建成功并生成一個全局唯一的 XID。
- XID 在微服務(wù)調(diào)用鏈路的上下文中傳播。
- RM 向 TC 注冊分支事務(wù),將其納入 XID 對應(yīng)全局事務(wù)的管轄。
- TM 向 TC 發(fā)起針對 XID 的全局提交或回滾決議。
- TC 調(diào)度 XID 下管轄的全部分支事務(wù)完成提交或回滾請求。
2.2 XA
在 XA 模式下,每一個 XA 事務(wù)都是一個事務(wù)參與者。分布式事務(wù)開啟之后
首先在一階段執(zhí)行“xa start”、“業(yè)務(wù) SQL”、“xa end”和 “xa prepare” 完成 XA 事務(wù)的執(zhí)行和預(yù)提交;
二階段如果提交的話就執(zhí)行 “xa commit”,如果是回滾則執(zhí)行“xa rollback”。這樣便能保證所有 XA 事務(wù)都提交或者都回滾。
無論 Phase2 的決議是 commit 還是 rollback,事務(wù)性資源的鎖都要保持到 Phase2 完成才釋放。
一個正常運行的業(yè)務(wù),大概率是 90% 以上的事務(wù)最終應(yīng)該是成功提交的,我們是否可以在 Phase1 就將本地事務(wù)提交呢?這樣 90% 以上的情況下,可以省去 Phase2 持鎖的時間,整體提高效率。
分支事務(wù)中數(shù)據(jù)的本地鎖由本地事務(wù)管理,在分支事務(wù) Phase1 結(jié)束時釋放,這時候其他本地事務(wù)就能讀取到最新的數(shù)據(jù)。 - 同時,隨著本地事務(wù)結(jié)束,連接也得以釋放。 - 分支事務(wù)中數(shù)據(jù)的全局鎖在事務(wù)協(xié)調(diào)器管理,在決議 Phase2 全局提交時,全局鎖馬上可以釋放,注意這里是先釋放鎖,再進(jìn)行分支事務(wù)的提交過程。只有在決議全局回滾的情況下,全局鎖才被持有至分支的 Phase2 結(jié)束,即所有分支事務(wù)回滾結(jié)束。
這個設(shè)計,極大地減少了分支事務(wù)對資源(數(shù)據(jù)和連接)的鎖定時間,給整體并發(fā)和吞吐的提升提供了基礎(chǔ)。但是分布式事務(wù)的隔離級別變化了
XA 模式和 下面的AT 模式一樣是一種對業(yè)務(wù)無侵入性的解決方案;但與 AT 模式不同的是,XA 模式將快照數(shù)據(jù)和行鎖等通過 XA 指令委托給了數(shù)據(jù)庫來完成,這樣 XA 模式實現(xiàn)更加輕量化
2.3 AT
AT 模式是一種無侵入的分布式事務(wù)解決方案。在 AT 模式下,用戶只需關(guān)注自己的“業(yè)務(wù) SQL”,用戶的 “業(yè)務(wù) SQL” 就是全局事務(wù)一階段,Seata 框架會自動生成事務(wù)的二階段提交和回滾操作。
一階段
首先,應(yīng)用要使用 Seata 的 JDBC 數(shù)據(jù)源代理,也就是前面提到的 RM 概念,所有對 DB 的操作都是通過 Seata RM 代理完成。在這層代理中,Seata 會自動控制 SQL 的執(zhí)行,提交,回滾。
Seata代理會把業(yè)務(wù)數(shù)據(jù)在更新前后的數(shù)據(jù)鏡像(beforeImage & afterImage)組織成回滾日志,利用本地事務(wù)的 ACID 特性,將業(yè)務(wù)數(shù)據(jù)的更新和回滾日志的寫入在同一個本地事務(wù)中提交。這樣,可以保證:任何提交的業(yè)務(wù)數(shù)據(jù)的更新一定有相應(yīng)的回滾日志存在。
然后,本地事務(wù)在提交之前, 還需要通過 RM 向 TC 注冊本地分支,這個注冊過程中會根據(jù)剛才執(zhí)行的 SQL 拿到所有涉及到的數(shù)據(jù)主鍵,以 resourceId + tableName + rowPK 作為鎖的 key,向 TC 申請所有涉及數(shù)據(jù)的寫鎖,當(dāng)獲得所有相關(guān)數(shù)據(jù)的寫鎖后,再執(zhí)行本地事務(wù)的 Commit 過程。如果有任何一行數(shù)據(jù)的寫鎖沒有拿到的話,TC 會以 fastfail 的方式回復(fù)該 RM,RM 會以重試 + 超時機(jī)制重復(fù)該過程,直到超時。
完成本地事務(wù)后,RM 會向 TC 匯報本地事務(wù)的執(zhí)行情況,并完成業(yè)務(wù) RPC 的調(diào)用過程。
二階段
case1:如果 TM 決議是全局提交,此時分支事務(wù)實際上已經(jīng)完成提交,TC 立刻釋放該全局事務(wù)的所有鎖,然后異步調(diào)用 RM 清理回滾日志,Phase2 可以非??焖俚赝瓿?。
case2:如果決議是全局回滾,RM 收到協(xié)調(diào)器發(fā)來的回滾請求,通過 XID 和 Branch ID 找到相應(yīng)的回滾日志記錄,通過回滾記錄生成反向的更新 SQL 并執(zhí)行,以完成分支的回滾。當(dāng)分支回滾順利結(jié)束時,通知 TC 回滾完成,這時候 TC 才釋放該分支事務(wù)相關(guān)的所有鎖。
注:RM 在進(jìn)行回滾時,會先跟 afterImage 進(jìn)行比較: - 如果一致:則執(zhí)行逆向 SQL - 如果不一致: 再跟 beforeImage 進(jìn)行比較 - 如果一致:說明沒必要執(zhí)行回滾 SQL 了,數(shù)據(jù)已經(jīng)恢復(fù)了 - 如果不一致:說明出現(xiàn)了臟數(shù)據(jù),這時候就拋出異常,需要人工處理
2.4 TCC
TCC 模式需要用戶根據(jù)自己的業(yè)務(wù)場景實現(xiàn) Try、Confirm 和 Cancel 三個操作;事務(wù)發(fā)起方先在 TC 中注冊全局事務(wù),然后在一階段執(zhí)行 Try 方法,在二階段提交的話 TC 會去執(zhí)行各個 RM 的 Confirm 方法,二階段回滾則 TC 會去執(zhí)行各個 RM 的 Cancel 方法。
與 AT 模式一樣,Seata 會給實際方法的執(zhí)行加切面,該切面會攔截所有對 TCC 接口的調(diào)用。在調(diào)用 Try 接口時,如果發(fā)現(xiàn)處在全局事務(wù)中,切面會先向 TC 注冊一個分支事務(wù),和 AT 不同的是TCC 注冊分支事務(wù)是不加鎖的,注冊完成后去執(zhí)行原來的 RPC 調(diào)用。當(dāng)請求鏈路調(diào)用完成后,TC 通過分支事務(wù)的資源 ID 回調(diào)到正確的參與者去執(zhí)行對應(yīng) TCC 資源的 Confirm 或 Cancel 方法。
TCC 模式的整體框架相對于 AT 來說更加簡單,主要是掃描 TCC 接口,注冊資源,攔截接口調(diào)用,注冊分支事務(wù),最后回調(diào)二階段接口。最核心的實際上是 TCC 接口的實現(xiàn)邏輯。
1)使用原則
從 TCC 模型的框架可以發(fā)現(xiàn),TCC 模型的核心在于 TCC 接口的設(shè)計。用戶在接入 TCC 時,大部分工作都集中在如何實現(xiàn) TCC 服務(wù)上。這就是 TCC 模式最主要的問題,對業(yè)務(wù)侵入比較大,要花很大的功夫來實現(xiàn) TCC 服務(wù)。
設(shè)計一套 TCC 接口最重要的是什么?主要有兩點,第一點,需要將操作分成兩階段完成。TCC(Try-Confirm-Cancel)分布式事務(wù)模型相對于 XA 等傳統(tǒng)模型,其特征在于它不依賴 RM 對分布式事務(wù)的支持,而是通過對業(yè)務(wù)邏輯的分解來實現(xiàn)分布式事務(wù)。
TCC 分布式事務(wù)模型需要業(yè)務(wù)系統(tǒng)提供三段業(yè)務(wù)邏輯: 1. 初步操作 Try:完成所有業(yè)務(wù)檢查,預(yù)留必須的業(yè)務(wù)資源。 2. 確認(rèn)操作 Confirm:真正執(zhí)行的業(yè)務(wù)邏輯,不做任何業(yè)務(wù)檢查,只使用 Try 階段預(yù)留的業(yè)務(wù)資源。因此,只要 Try 操作成功,Confirm 必須能成功。另外,Confirm 操作需滿足冪等性,保證一筆分布式事務(wù)能且只能成功一次。 3. 取消操作 Cancel:釋放 Try 階段預(yù)留的業(yè)務(wù)資源。同樣的,Cancel 操作也需要滿足冪等性。因此,TCC 模型的隔離性思想就是通過業(yè)務(wù)的改造,在第一階段結(jié)束之后,從底層數(shù)據(jù)庫資源層面的加鎖過渡為上層業(yè)務(wù)層面的加鎖,釋放底層數(shù)據(jù)庫鎖資源,放寬分布式事務(wù)鎖協(xié)議,將鎖的粒度降到最低,以最大限度提高業(yè)務(wù)并發(fā)性能。
第二點,就是要根據(jù)自身的業(yè)務(wù)模型去控制并發(fā),Seata 框架本身僅提供兩階段原子提交協(xié)議,保證分布式事務(wù)原子性。事務(wù)的隔離需要交給業(yè)務(wù)邏輯來實現(xiàn)。隔離的本質(zhì)就是控制并發(fā),防止并發(fā)事務(wù)操作相同資源而引起的結(jié)果錯亂。例如:“賬戶 A 上有 100 元,事務(wù) T1 要扣除其中的 30 元,事務(wù) T2 也要扣除 30 元,出現(xiàn)并發(fā)”。在第一階段 Try 操作中,需要先利用數(shù)據(jù)庫資源層面的加鎖,檢查賬戶可用余額,如果余額充足,則預(yù)留業(yè)務(wù)資源加到各自的凍結(jié)里,扣除本次交易金額,一階段結(jié)束后,雖然數(shù)據(jù)庫層面資源鎖被釋放了,但這筆資金被業(yè)務(wù)隔離,不允許除本事務(wù)之外的其它并發(fā)事務(wù)動用。
2)異??刂?/h4>
空回滾
空回滾就是對于一個分布式事務(wù),在沒有調(diào)用 TCC 資源 Try 方法的情況下,調(diào)用了二階段的 Cancel 方法,Cancel 方法需要識別出這是一個空回滾,然后直接返回成功。
Cancel 要識別出空回滾,直接返回成功。那關(guān)鍵就是要識別出這個空回滾。思路很簡單就是需要知道一階段是否執(zhí)行,如果執(zhí)行了,那就是正?;貪L;如果沒執(zhí)行,那就是空回滾。因此,需要一張額外的事務(wù)控制表,其中有分布式事務(wù) ID 和分支事務(wù) ID,第一階段 Try 方法里會插入一條記錄,表示一階段執(zhí)行了。Cancel 接口里讀取該記錄,如果該記錄存在,則正常回滾;如果該記錄不存在,則是空回滾。
懸掛
懸掛就是對于一個分布式事務(wù),其二階段 Cancel 接口比 Try 接口先執(zhí)行。因為允許空回滾的原因,Cancel 接口認(rèn)為 Try 接口沒執(zhí)行,空回滾直接返回成功,對于 Seata 框架來說,認(rèn)為分布式事務(wù)的二階段接口已經(jīng)執(zhí)行成功,整個分布式事務(wù)就結(jié)束了。但是這之后 Try 方法才真正開始執(zhí)行,預(yù)留業(yè)務(wù)資源,回想一下前面提到事務(wù)并發(fā)控制的業(yè)務(wù)加鎖,對于一個 Try 方法預(yù)留的業(yè)務(wù)資源,只有該分布式事務(wù)才能使用,然而 Seata 框架認(rèn)為該分布式事務(wù)已經(jīng)結(jié)束,也就是說,當(dāng)出現(xiàn)這種情況時,該分布式事務(wù)第一階段預(yù)留的業(yè)務(wù)資源就再也沒有人能夠處理了。
比如在 RPC 調(diào)用時,先注冊分支事務(wù),再執(zhí)行 RPC 調(diào)用,如果此時 RPC 調(diào)用的網(wǎng)絡(luò)發(fā)生擁堵,通常 RPC 調(diào)用是有超時時間的,RPC 超時以后,發(fā)起方就會通知 TC 回滾該分布式事務(wù),可能回滾完成后,RPC 請求才到達(dá)參與者,真正執(zhí)行,從而造成懸掛。
冪等
冪等就是對于同一個分布式事務(wù)的同一個分支事務(wù),重復(fù)去調(diào)用該分支事務(wù)的第二階段接口,因此,要求 TCC 的二階段 Confirm 和 Cancel 接口保證冪等,不會重復(fù)使用或者釋放資源。如果冪等控制沒有做好,很有可能導(dǎo)致資損等嚴(yán)重問題。
解決思路
Try 方法主要需要考慮兩個問題,一個是 Try 方法需要能夠告訴二階段接口,已經(jīng)預(yù)留業(yè)務(wù)資源成功。第二個是需要檢查第二階段是否已經(jīng)執(zhí)行完成,如果已完成,則不再執(zhí)行
Confirm 方法。因為 Confirm 方法不允許空回滾,也就是說,Confirm 方法一定要在 Try 方法之后執(zhí)行。因此,Confirm 方法只需要關(guān)注重復(fù)提交的問題。需要一張事務(wù)執(zhí)行記錄表,可以先鎖定事務(wù)記錄,如果事務(wù)記錄為空,則說明是一個空提交,不允許,終止執(zhí)行。如果事務(wù)記錄不為空,則繼續(xù)檢查狀態(tài)是否為初始化,如果是,則說明一階段正確執(zhí)行,那二階段正常執(zhí)行即可。如果狀態(tài)是已提交,則認(rèn)為是重復(fù)提交,直接返回成功即可;如果狀態(tài)是已回滾,也是一個異常,一個已回滾的事務(wù),不能重新提交,需要能夠攔截到這種異常情況,并報警。
Cancel 方法。因為 Cancel 方法允許空回滾,并且要在先執(zhí)行的情況下,讓 Try 方法感知到 Cancel 已經(jīng)執(zhí)行,所以和 Confirm 方法略有不同。首先依然是鎖定事務(wù)記錄。如果事務(wù)記錄為空,則認(rèn)為 Try 方法還沒執(zhí)行,即是空回滾。空回滾的情況下,應(yīng)該先插入一條事務(wù)記錄,確保后續(xù)的 Try 方法不會再執(zhí)行。如果插入成功,則說明 Try 方法還沒有執(zhí)行,空回滾繼續(xù)執(zhí)行。如果插入失敗,則認(rèn)為Try 方法正在執(zhí)行,等待 TC 的重試即可。如果一開始讀取事務(wù)記錄不為空,則說明 Try 方法已經(jīng)執(zhí)行完畢,再檢查狀態(tài)是否為初始化,如果是,則還沒有執(zhí)行過其他二階段方法,正常執(zhí)行 Cancel 邏輯。如果狀態(tài)為已回滾,則說明這是重復(fù)調(diào)用,允許冪等,直接返回成功即可。如果狀態(tài)為已提交,則同樣是一個異常,一個已提交的事務(wù),不能再次回滾
2.5 Saga
Saga 模式是 Seata 即將開源的長事務(wù)解決方案。在 Saga 模式下,分布式事務(wù)內(nèi)有多個參與者,每一個參與者都是一個沖正補(bǔ)償服務(wù),需要用戶根據(jù)業(yè)務(wù)場景實現(xiàn)其正向操作和逆向回滾操作。
分布式事務(wù)執(zhí)行過程中,依次執(zhí)行各參與者的正向操作,如果所有正向操作均執(zhí)行成功,那么分布式事務(wù)提交。如果任何一個正向操作執(zhí)行失敗,那么分布式事務(wù)會去退回去執(zhí)行前面各參與者的逆向回滾操作,回滾已提交的參與者,使分布式事務(wù)回到初始狀態(tài)。
Saga 正向服務(wù)與補(bǔ)償服務(wù)也需要業(yè)務(wù)開發(fā)者實現(xiàn)。有點像是 TCC 模式將 Try 過程和 Confirm 過程合并,所有參與者直接執(zhí)行 Try + Confirm,如果有人失敗了,就反向依次 Cancel。
由于該模式主要用于長事務(wù)場景,所以通常是由事件驅(qū)動的,各個參與者之間是異步執(zhí)行的。
Saga 模式適用于業(yè)務(wù)流程長且需要保證事務(wù)最終一致性的業(yè)務(wù)系統(tǒng),Saga 模式一階段就會提交本地事務(wù),無鎖、長流程情況下可以保證性能
Saga模式的優(yōu)勢是:
- 一階段提交本地數(shù)據(jù)庫事務(wù),無鎖,高性能;
- 參與者可以采用事務(wù)驅(qū)動異步執(zhí)行,高吞吐;
- 補(bǔ)償服務(wù)即正向服務(wù)的“反向”,易于理解,易于實現(xiàn);
缺點:Saga 模式由于一階段已經(jīng)提交本地數(shù)據(jù)庫事務(wù),且沒有進(jìn)行“預(yù)留”動作,所以不能保證隔離性。
事務(wù)隔離
縱觀 Seata 提供的所有分支事務(wù)模式, 除了 AT 模式和 XA 模式可以運行在讀已提交的隔離級別下, 其他模式都是運行在讀未提交的級別下。在有必要時,應(yīng)用需要通過業(yè)務(wù)邏輯的巧妙設(shè)定,來解決分布式事務(wù)隔離級別帶來的問題
AT 模式通過全局寫排他鎖,來保證事務(wù)間的寫隔離,將全局事務(wù)默認(rèn)定義在讀未提交的隔離級別上,全局事務(wù)讀未提交,并不是說本地事務(wù)的db數(shù)據(jù)沒有正常提交,而是指全局事務(wù)二階段commit | rollback未真正處理完(即未釋放全局鎖),而且這時候其他事務(wù)會讀到一階段提交的內(nèi)容。
有些應(yīng)用如果需要達(dá)到全局的讀已提交,AT 也提供了相應(yīng)的機(jī)制來達(dá)到目的,那就是 select for update + @GlobalLock, 當(dāng)執(zhí)行該命令時 RM 會去 TC 確認(rèn)該鎖是否由他人占有, 這樣如果有一個分布式事務(wù) T1 正在進(jìn)行中時, 另一個事務(wù) T2 會因為發(fā)現(xiàn)鎖沖突而阻塞后續(xù)代碼的執(zhí)行, 當(dāng)前面的分布式事務(wù) T1 結(jié)束時, 釋放了相應(yīng)的資源鎖, T2 才能讀取到相應(yīng)的數(shù)據(jù), 這樣就達(dá)到讀已提交的效果
2.6 消息組件
利用 MQ 組件實現(xiàn)的二階段提交。此方案涉及 3 個模塊:
- 上游應(yīng)用,執(zhí)行業(yè)務(wù)并發(fā)送 MQ 消息。
- 可靠消息服務(wù)和 MQ 消息組件,協(xié)調(diào)上下游消息的傳遞,并確保上下游數(shù)據(jù)的一致性。
- 下游應(yīng)用,監(jiān)聽 MQ 的消息并執(zhí)行自身業(yè)務(wù)。
上游應(yīng)用將本地業(yè)務(wù)執(zhí)行和消息發(fā)送綁定在同一個本地事務(wù)中,保證要么本地操作成功并發(fā)送 MQ 消息,要么兩步操作都失敗并回滾。
- 上游應(yīng)用發(fā)送待確認(rèn)消息到可靠消息系統(tǒng)
- 可靠消息系統(tǒng)保存待確認(rèn)消息并返回
- 上游應(yīng)用執(zhí)行本地業(yè)務(wù)
- 上游應(yīng)用通知可靠消息系統(tǒng)確認(rèn)業(yè)務(wù)已執(zhí)行并發(fā)送消息。
- 可靠消息系統(tǒng)修改消息狀態(tài)為發(fā)送狀態(tài)并將消息投遞到 MQ 中間件。
- 下游應(yīng)用監(jiān)聽 MQ 消息組件并獲取消息
- 下游應(yīng)用根據(jù) MQ 消息體信息處理本地業(yè)務(wù)
- 下游應(yīng)用向 MQ 組件自動發(fā)送 ACK 確認(rèn)消息被消費
- 下游應(yīng)用通知可靠消息系統(tǒng)消息被成功消費,可靠消息將該消息狀態(tài)更改為已完成。
異常處理
上游異常
可靠消息服務(wù)定時監(jiān)聽消息的狀態(tài),如果存在狀態(tài)為待確認(rèn)并且超時的消息,則表示上游應(yīng)用和可靠消息交互中的步驟 4 或者 5 出現(xiàn)異常。
- 可靠消息查詢超時的待確認(rèn)狀態(tài)的消息
- 向上游應(yīng)用查詢業(yè)務(wù)執(zhí)行的情況
- 業(yè)務(wù)未執(zhí)行,則刪除該消息,保證業(yè)務(wù)和可靠消息服務(wù)的一致性。業(yè)務(wù)已執(zhí)行,則修改消息狀態(tài)為已發(fā)送,并發(fā)送消息到 MQ 組件。
下游異常
- 可靠消息服務(wù)定時查詢狀態(tài)為已發(fā)送并超時的消息
- 可靠消息將消息重新投遞到 MQ 組件中
- 下游應(yīng)用監(jiān)聽消息,在滿足冪等性的條件下,重新執(zhí)行業(yè)務(wù)。
- 下游應(yīng)用通知可靠消息服務(wù)該消息已經(jīng)成功消費。
實際過程中,還需要引入人工干預(yù)功能。比如引入重發(fā)次數(shù)限制,超過重發(fā)次數(shù)限制的將消息修改為死亡消息,等待人工處理。
3 總結(jié)
3.1 sql支持上
AT其實就是一個自實現(xiàn)的XA事務(wù),其實可以知道,AT在sql支持上遠(yuǎn)不及XA模式,AT需要做sql解析背后的實現(xiàn)只能自己解決,目前只能靠社區(qū)的貢獻(xiàn)者來提供解決方案,這是一個長期的關(guān)鍵性的問題,也有很多用戶選擇在AT模式上重寫sql來獲取AT模式的支持,sql支持上XA是完勝的。
3.2 隔離性
AT模式是通過解析sql獲取涉及的主鍵id,生成行鎖。也就是AT模式的隔離靠的是全局鎖來保證的,粒度細(xì)至行級。鎖信息存儲在seata server側(cè)。XA的隔離級別是由本地數(shù)據(jù)庫保證,鎖存儲在各個本地數(shù)據(jù)庫中。由于XA模式一旦執(zhí)行了prepare后,再也無法重入這個XA事務(wù)也無法跟其他XA事務(wù)共享鎖,因為XA協(xié)議僅僅是通過XID來start一個事務(wù),本身不存在分支事務(wù)的說法。也就是說他只管自己
3.3 入侵性
通過上面的信息可以發(fā)現(xiàn)誰更底層,入侵性則更小,所以由數(shù)據(jù)庫自身支持的XA模式來說,入侵性無疑最小,使用成本最低。 XA的RM實際是在數(shù)據(jù)庫,而AT則是以中間件層部署在應(yīng)用這一側(cè)的,不依賴數(shù)據(jù)庫本身的協(xié)議支持,這點對于微服務(wù)架構(gòu)來說是至關(guān)重要的。應(yīng)用層不需要為本地事務(wù)和分布式事務(wù)多類不同場景來適配多套不通的驅(qū)動
3.4 補(bǔ)償性事務(wù)的問題
本質(zhì)上seata框架支持了3大補(bǔ)償事務(wù)模式,AT, TCC,Saga都是補(bǔ)償型的。補(bǔ)償型事務(wù)處理機(jī)制是構(gòu)建在事務(wù)資源之上的,事務(wù)資源本身對分布式事務(wù)是無感知的,事務(wù)資源對分布式事務(wù)無感知存在一個根本性問題,就是無法做到真正的全局一致性。 比如一條庫存記錄,處在補(bǔ)償型事務(wù)處理過程中,由100扣減為50,此時倉庫管理員連接數(shù)據(jù)庫查看就會查詢到50,之后事務(wù)異常回滾,庫存就會被補(bǔ)償回滾為100,顯然倉庫管理員查詢到的50就是臟數(shù)據(jù)。那XA的價值是什么,與補(bǔ)償型事務(wù)不同,XA協(xié)議要求事務(wù)資源本身提供對規(guī)范和協(xié)議的支持。因為事務(wù)資源感知并參與分布式事務(wù)處理過程,所以事務(wù)資源可以保證從任意視角對數(shù)據(jù)的訪問有效隔離,比如上面XA事務(wù)處理過程中,中間態(tài)的50是不會被查詢到的(當(dāng)然隔離級別要在讀已提交以上),以此來滿足全局一致性。?