分布式事務(wù)的7種解決方案是怎樣的,相信很多沒(méi)有經(jīng)驗的人對此束手無(wú)策,為此本文總結了問(wèn)題出現的原因和解決方法,通過(guò)這篇文章希望你能解決這個(gè)問(wèn)題。
隨著(zhù)業(yè)務(wù)的快速發(fā)展、業(yè)務(wù)復雜度越來(lái)越高,幾乎每個(gè)公司的系統都會(huì )從單體走向分布式,特別是轉向微服務(wù)架構。隨之而來(lái)就必然遇到分布式事務(wù)這個(gè)難題。
這篇文章首先介紹了相關(guān)的基礎理論,然后總結了最經(jīng)典的事務(wù)方案,最后給出了子事務(wù)亂序執行(冪等、空補償、懸掛問(wèn)題)的解決方案,分享給大家。
在講解具體方案之前,我們先了解一下分布式事務(wù)所涉及到的基礎理論知識。
我們拿轉賬作為例子,A需要轉100元給B,那么需要給A的余額-100元,給B的余額+100元,整個(gè)轉賬要保證,A-100和B+100同時(shí)成功,或者同時(shí)失敗??纯丛诟鞣N場(chǎng)景下,是如何解決這個(gè)問(wèn)題的。
把多條語(yǔ)句作為一個(gè)整體進(jìn)行操作的功能,被稱(chēng)為數據庫事務(wù)。數據庫事務(wù)可以確保該事務(wù)范圍內的所有操作都可以全部成功或者全部失敗。
事務(wù)具有 4 個(gè)屬性:原子性、一致性、隔離性、持久性。這四個(gè)屬性通常稱(chēng)為 ACID 特性。
Atomicity(原子性):一個(gè)事務(wù)中的所有操作,要么全部完成,要么全部不完成,不會(huì )結束在中間某個(gè)環(huán)節。事務(wù)在執行過(guò)程中發(fā)生錯誤,會(huì )被恢復到事務(wù)開(kāi)始前的狀態(tài),就像這個(gè)事務(wù)從來(lái)沒(méi)有執行過(guò)一樣。
Consistency(一致性):在事務(wù)開(kāi)始之前和事務(wù)結束以后,數據庫的完整性沒(méi)有被破壞。完整性包括外鍵約束、應用定義的等約束不會(huì )被破壞。
Isolation(隔離性):數據庫允許多個(gè)并發(fā)事務(wù)同時(shí)對其數據進(jìn)行讀寫(xiě)和修改的能力,隔離性可以防止多個(gè)事務(wù)并發(fā)執行時(shí)由于交叉執行而導致數據的不一致。
Durability(持久性):事務(wù)處理結束后,對數據的修改就是永久的,即便系統故障也不會(huì )丟失。
假如我們的業(yè)務(wù)系統不復雜,可以在一個(gè)數據庫、一個(gè)服務(wù)內對數據進(jìn)行修改,完成轉賬,那么,我們可以利用數據庫事務(wù),保證轉賬業(yè)務(wù)的正確完成。
銀行跨行轉賬業(yè)務(wù)是一個(gè)典型分布式事務(wù)場(chǎng)景,假設A需要跨行轉賬給B,那么就涉及兩個(gè)銀行的數據,無(wú)法通過(guò)一個(gè)數據庫的本地事務(wù)保證轉賬的ACID,只能夠通過(guò)分布式事務(wù)來(lái)解決。
分布式事務(wù)就是指事務(wù)的發(fā)起者、資源及資源管理器和事務(wù)協(xié)調者分別位于分布式系統的不同節點(diǎn)之上。在上述轉賬的業(yè)務(wù)中,用戶(hù)A-100操作和用戶(hù)B+100操作不是位于同一個(gè)節點(diǎn)上。本質(zhì)上來(lái)說(shuō),分布式事務(wù)就是為了保證在分布式場(chǎng)景下,數據操作的正確執行。
分布式事務(wù)在分布式環(huán)境下,為了滿(mǎn)足可用性、性能與降級服務(wù)的需要,降低一致性與隔離性的要求,一方面遵循 BASE 理論(BASE相關(guān)理論,涉及內容非常多,感興趣的同學(xué),可以參考BASE理論):
基本業(yè)務(wù)可用性(Basic Availability)
柔性狀態(tài)(Soft state)
最終一致性(Eventual consistency)
同樣的,分布式事務(wù)也部分遵循 ACID 規范:
原子性:嚴格遵循
一致性:事務(wù)完成后的一致性嚴格遵循;事務(wù)中的一致性可適當放寬
隔離性:并行事務(wù)間不可影響;事務(wù)中間結果可見(jiàn)性允許安全放寬
持久性:嚴格遵循
由于分布式事務(wù)方案,無(wú)法做到完全的ACID的保證,沒(méi)有一種完美的方案,能夠解決掉所有業(yè)務(wù)問(wèn)題。因此在實(shí)際應用中,會(huì )根據業(yè)務(wù)的不同特性,選擇最適合的分布式事務(wù)方案。
XA是由X/Open組織提出的分布式事務(wù)的規范,XA規范主要定義了(全局)事務(wù)管理器(TM)和(局部)資源管理器(RM)之間的接口。本地的數據庫如在XA中扮演的是RM角色
XA一共分為兩階段:
第一階段(prepare):即所有的參與者RM準備執行事務(wù)并鎖住需要的資源。參與者ready時(shí),向TM報告已準備就緒。
第二階段 (commit/rollback):當事務(wù)管理者(TM)確認所有參與者(RM)都ready后,向所有參與者發(fā)送commit命令。
目前主流的數據庫基本都支持XA事務(wù),包括mysql、oracle、sqlserver、postgre
XA 事務(wù)由一個(gè)或多個(gè)資源管理器(RM)、一個(gè)事務(wù)管理器(TM)和一個(gè)應用程序(ApplicationProgram)組成。
這里的RM、TM、AP三個(gè)角色是經(jīng)典的角色劃分,會(huì )貫穿后續Saga、Tcc等事務(wù)模式。
把上面的轉賬作為例子,一個(gè)成功完成的XA事務(wù)時(shí)序圖如下:
如果有任何一個(gè)參與者prepare失敗,那么TM會(huì )通知所有完成prepare的參與者進(jìn)行回滾。
XA事務(wù)的特點(diǎn)是:
簡(jiǎn)單易理解,開(kāi)發(fā)較容易
對資源進(jìn)行了長(cháng)時(shí)間的鎖定,并發(fā)度低
如果讀者想要進(jìn)一步研究XA,go語(yǔ)言以及PHP、Python、Java、C#、Node等都可參考DTM
Saga是這一篇數據庫論文sagas提到的一個(gè)方案。其核心思想是將長(cháng)事務(wù)拆分為多個(gè)本地短事務(wù),由Saga事務(wù)協(xié)調器協(xié)調,如果正常結束那就正常完成,如果某個(gè)步驟失敗,則根據相反順序一次調用補償操作。
把上面的轉賬作為例子,一個(gè)成功完成的SAGA事務(wù)時(shí)序圖如下:
Saga一旦到了Cancel階段,那么Cancel在業(yè)務(wù)邏輯上是不允許失敗了。如果因為網(wǎng)絡(luò )或者其他臨時(shí)故障,導致沒(méi)有返回成功,那么TM會(huì )不斷重試,直到Cancel返回成功。
Saga事務(wù)的特點(diǎn):
并發(fā)度高,不用像XA事務(wù)那樣長(cháng)期鎖定資源
需要定義正常操作以及補償操作,開(kāi)發(fā)量比XA大
一致性較弱,對于轉賬,可能發(fā)生A用戶(hù)已扣款,最后轉賬又失敗的情況
論文里面的SAGA內容較多,包括兩種恢復策略,包括分支事務(wù)并發(fā)執行,我們這里的討論,僅包括最簡(jiǎn)單的SAGA
SAGA適用的場(chǎng)景較多,長(cháng)事務(wù)適用,對中間結果不敏感的業(yè)務(wù)場(chǎng)景適用
如果讀者想要進(jìn)一步研究SAGA,可參考DTM,里面包括了SAGA成功、失敗回滾的例子,還包括各類(lèi)網(wǎng)絡(luò )異常的處理。
關(guān)于 TCC(Try-Confirm-Cancel)的概念,最早是由 Pat Helland 于 2007 年發(fā)表的一篇名為《Life beyond Distributed Transactions:an Apostate’s Opinion》的論文提出。
TCC分為3個(gè)階段
Try 階段:嘗試執行,完成所有業(yè)務(wù)檢查(一致性), 預留必須業(yè)務(wù)資源(準隔離性)
Confirm 階段:確認執行真正執行業(yè)務(wù),不作任何業(yè)務(wù)檢查,只使用 Try 階段預留的業(yè)務(wù)資源,Confirm 操作要求具備冪等設計,Confirm 失敗后需要進(jìn)行重試。
Cancel 階段:取消執行,釋放 Try 階段預留的業(yè)務(wù)資源。Cancel 階段的異常和 Confirm 階段異常處理方案基本上一致,要求滿(mǎn)足冪等設計。
把上面的轉賬作為例子,通常會(huì )在Try里面凍結金額,但不扣款,Confirm里面扣款,Cancel里面解凍金額,一個(gè)成功完成的TCC事務(wù)時(shí)序圖如下:
TCC的Confirm/Cancel階段在業(yè)務(wù)邏輯上是不允許返回失敗的,如果因為網(wǎng)絡(luò )或者其他臨時(shí)故障,導致不能返回成功,TM會(huì )不斷的重試,直到Confirm/Cancel返回成功。
TCC特點(diǎn)如下:
并發(fā)度較高,無(wú)長(cháng)期資源鎖定。
開(kāi)發(fā)量較大,需要提供Try/Confirm/Cancel接口。
一致性較好,不會(huì )發(fā)生SAGA已扣款最后又轉賬失敗的情況
TCC適用于訂單類(lèi)業(yè)務(wù),對中間狀態(tài)有約束的業(yè)務(wù)
如果讀者想要進(jìn)一步研究TCC,可參考DTM
本地消息表這個(gè)方案最初是 ebay 架構師 Dan Pritchett 在 2008 年發(fā)表給 ACM 的文章。設計核心是將需要分布式處理的任務(wù)通過(guò)消息的方式來(lái)異步確保執行。
大致流程如下:
寫(xiě)本地消息和業(yè)務(wù)操作放在一個(gè)事務(wù)里,保證了業(yè)務(wù)和發(fā)消息的原子性,要么他們全都成功,要么全都失敗。
容錯機制:
扣減余額事務(wù) 失敗時(shí),事務(wù)直接回滾,無(wú)后續步驟
輪序生產(chǎn)消息失敗, 增加余額事務(wù)失敗都會(huì )進(jìn)行重試
本地消息表的特點(diǎn):
長(cháng)事務(wù)僅需要分拆成多個(gè)任務(wù),使用簡(jiǎn)單
生產(chǎn)者需要額外的創(chuàng )建消息表
每個(gè)本地消息表都需要進(jìn)行輪詢(xún)
消費者的邏輯如果無(wú)法通過(guò)重試成功,那么還需要更多的機制,來(lái)回滾操作
適用于可異步執行的業(yè)務(wù),且后續操作無(wú)需回滾的業(yè)務(wù)
在上述的本地消息表方案中,生產(chǎn)者需要額外創(chuàng )建消息表,還需要對本地消息表進(jìn)行輪詢(xún),業(yè)務(wù)負擔較重。阿里開(kāi)源的RocketMQ 4.3之后的版本正式支持事務(wù)消息,該事務(wù)消息本質(zhì)上是把本地消息表放到RocketMQ上,解決生產(chǎn)端的消息發(fā)送與本地事務(wù)執行的原子性問(wèn)題。
事務(wù)消息發(fā)送及提交:
發(fā)送消息(half消息)
服務(wù)端存儲消息,并響應消息的寫(xiě)入結果
根據發(fā)送結果執行本地事務(wù)(如果寫(xiě)入失敗,此時(shí)half消息對業(yè)務(wù)不可見(jiàn),本地邏輯不執行)
根據本地事務(wù)狀態(tài)執行Commit或者Rollback(Commit操作發(fā)布消息,消息對消費者可見(jiàn))
正常發(fā)送的流程圖如下:
補償流程:
對沒(méi)有Commit/Rollback的事務(wù)消息(pending狀態(tài)的消息),從服務(wù)端發(fā)起一次“回查”
Producer收到回查消息,返回消息對應的本地事務(wù)的狀態(tài),為Commit或者Rollback
事務(wù)消息方案與本地消息表機制非常類(lèi)似,區別主要在于原先相關(guān)的本地表操作替換成了一個(gè)反查接口
事務(wù)消息特點(diǎn)如下:
長(cháng)事務(wù)僅需要分拆成多個(gè)任務(wù),并提供一個(gè)反查接口,使用簡(jiǎn)單
消費者的邏輯如果無(wú)法通過(guò)重試成功,那么還需要更多的機制,來(lái)回滾操作
適用于可異步執行的業(yè)務(wù),且后續操作無(wú)需回滾的業(yè)務(wù)
如果讀者想要進(jìn)一步研究事務(wù)消息,可參考DTM,也可以參考Rocketmq
發(fā)起通知方通過(guò)一定的機制最大努力將業(yè)務(wù)處理結果通知到接收方。具體包括:
有一定的消息重復通知機制。因為接收通知方可能沒(méi)有接收到通知,此時(shí)要有一定的機制對消息重復通知。
消息校對機制。如果盡最大努力也沒(méi)有通知到接收方,或者接收方消費消息后要再次消費,此時(shí)可由接收方主動(dòng)向通知方查詢(xún)消息信息來(lái)滿(mǎn)足需求。
前面介紹的的本地消息表和事務(wù)消息都屬于可靠消息,與這里介紹的最大努力通知有什么不同?
可靠消息一致性,發(fā)起通知方需要保證將消息發(fā)出去,并且將消息發(fā)到接收通知方,消息的可靠性關(guān)鍵由發(fā)起通知方來(lái)保證。
最大努力通知,發(fā)起通知方盡最大的努力將業(yè)務(wù)處理結果通知為接收通知方,但是可能消息接收不到,此時(shí)需要接收通知方主動(dòng)調用發(fā)起通知方的接口查詢(xún)業(yè)務(wù)處理結果,通知的可靠性關(guān)鍵在接收通知方。
解決方案上,最大努力通知需要:
提供接口,讓接受通知放能夠通過(guò)接口查詢(xún)業(yè)務(wù)處理結果
消息隊列ACK機制,消息隊列按照間隔1min、5min、10min、30min、1h、2h、5h、10h的方式,逐步拉大通知間隔 ,直到達到通知要求的時(shí)間窗口上限。之后不再通知
最大努力通知適用于業(yè)務(wù)通知類(lèi)型,例如微信交易的結果,就是通過(guò)最大努力通知方式通知各個(gè)商戶(hù),既有回調通知,也有交易查詢(xún)接口
這是阿里開(kāi)源項目seata中的一種事務(wù)模式,在螞蟻金服也被稱(chēng)為FMT。優(yōu)點(diǎn)是該事務(wù)模式使用方式,類(lèi)似XA模式,業(yè)務(wù)無(wú)需編寫(xiě)各類(lèi)補償操作,回滾由框架自動(dòng)完成,缺點(diǎn)也類(lèi)似XA,存在較長(cháng)時(shí)間的鎖,不滿(mǎn)足高并發(fā)的場(chǎng)景。從性能的角度看,AT模式會(huì )比XA更高一些,但也帶來(lái)了臟回滾這樣的新問(wèn)題。有興趣的同學(xué)可以參考seata-AT
在分布式事務(wù)的各個(gè)環(huán)節都有可能出現網(wǎng)絡(luò )以及業(yè)務(wù)故障等問(wèn)題,這些問(wèn)題需要分布式事務(wù)的業(yè)務(wù)方做到防空回滾,冪等,防懸掛三個(gè)特性。
下面以TCC事務(wù)說(shuō)明這些異常情況:
空回滾:
在沒(méi)有調用 TCC 資源 Try 方法的情況下,調用了二階段的 Cancel 方法,Cancel 方法需要識別出這是一個(gè)空回滾,然后直接返回成功。
出現原因是當一個(gè)分支事務(wù)所在服務(wù)宕機或網(wǎng)絡(luò )異常,分支事務(wù)調用記錄為失敗,這個(gè)時(shí)候其實(shí)是沒(méi)有執行Try階段,當故障恢復后,分布式事務(wù)進(jìn)行回滾則會(huì )調用二階段的Cancel方法,從而形成空回滾。
冪等:
由于任何一個(gè)請求都可能出現網(wǎng)絡(luò )異常,出現重復請求,所以所有的分布式事務(wù)分支,都需要保證冪等性
懸掛:
懸掛就是對于一個(gè)分布式事務(wù),其二階段 Cancel 接口比 Try 接口先執行。
出現原因是在 RPC 調用分支事務(wù)try時(shí),先注冊分支事務(wù),再執行RPC調用,如果此時(shí) RPC 調用的網(wǎng)絡(luò )發(fā)生擁堵,RPC 超時(shí)以后,TM就會(huì )通知RM回滾該分布式事務(wù),可能回滾完成后,Try 的 RPC 請求才到達參與者真正執行。
下面看一個(gè)網(wǎng)絡(luò )異常的時(shí)序圖,更好的理解上述幾種問(wèn)題
業(yè)務(wù)處理請求4的時(shí)候,Cancel在Try之前執行,需要處理空回滾
業(yè)務(wù)處理請求6的時(shí)候,Cancel重復執行,需要冪等
業(yè)務(wù)處理請求8的時(shí)候,Try在Cancel后執行,需要處理懸掛
面對上述復雜的網(wǎng)絡(luò )異常情況,目前看到各家建議的方案都是業(yè)務(wù)方通過(guò)唯一鍵,去查詢(xún)相關(guān)聯(lián)的操作是否已完成,如果已完成則直接返回成功。相關(guān)的判斷邏輯較復雜,易出錯,業(yè)務(wù)負擔重。
在項目https://github.com/yedf/dtm中,出現了一種子事務(wù)屏障技術(shù),使用該技術(shù),能夠達到這個(gè)效果,看示意圖:
所有這些請求,到了子事務(wù)屏障后:不正常的請求,會(huì )被過(guò)濾;正常請求,通過(guò)屏障。開(kāi)發(fā)者使用子事務(wù)屏障之后,前面所說(shuō)的各種異常全部被妥善處理,業(yè)務(wù)開(kāi)發(fā)人員只需要關(guān)注實(shí)際的業(yè)務(wù)邏輯,負擔大大降低。
子事務(wù)屏障提供了方法ThroughBarrierCall,方法的原型為:
func ThroughBarrierCall(db *sql.DB, transInfo *TransInfo, busiCall BusiFunc)
業(yè)務(wù)開(kāi)發(fā)人員,在busiCall里面編寫(xiě)自己的相關(guān)邏輯,調用該函數。ThroughBarrierCall保證,在空回滾、懸掛等場(chǎng)景下,busiCall不會(huì )被調用;在業(yè)務(wù)被重復調用時(shí),有冪等控制,保證只被提交一次。
子事務(wù)屏障會(huì )管理TCC、SAGA、事務(wù)消息等,也可以擴展到其他領(lǐng)域
子事務(wù)屏障技術(shù)的原理是,在本地數據庫,建立分支事務(wù)狀態(tài)表sub_trans_barrier,唯一鍵為全局事務(wù)id-子事務(wù)id-子事務(wù)分支名稱(chēng)(try|confirm|cancel)
開(kāi)啟事務(wù)
如果是Try分支,則那么insert ignore插入gid-branchid-try,如果成功插入,則調用屏障內邏輯
如果是Confirm分支,那么insert ignore插入gid-branchid-confirm,如果成功插入,則調用屏障內邏輯
如果是Cancel分支,那么insert ignore插入gid-branchid-try,再插入gid-branchid-cancel,如果try未插入并且cancel插入成功,則調用屏障內邏輯
屏障內邏輯返回成功,提交事務(wù),返回成功
屏障內邏輯返回錯誤,回滾事務(wù),返回錯誤
在此機制下,解決了網(wǎng)絡(luò )異常相關(guān)的問(wèn)題
空補償控制--如果Try沒(méi)有執行,直接執行了Cancel,那么Cancel插入gid-branchid-try會(huì )成功,不走屏障內的邏輯,保證了空補償控制
冪等控制--任何一個(gè)分支都無(wú)法重復插入唯一鍵,保證了不會(huì )重復執行
防懸掛控制--Try在Cancel之后執行,那么插入的gid-branchid-try不成功,就不執行,保證了防懸掛控制
對于SAGA、事務(wù)消息等,也是類(lèi)似的機制。
子事務(wù)屏障技術(shù),為https://github.com/yedf/dtm首創(chuàng ),它的意義在于設計簡(jiǎn)單易實(shí)現的算法,提供了簡(jiǎn)單易用的接口,在首創(chuàng ),它的意義在于設計簡(jiǎn)單易實(shí)現的算法,提供了簡(jiǎn)單易用的接口,在這兩項的幫助下,開(kāi)發(fā)人員徹底的從網(wǎng)絡(luò )異常的處理中解放出來(lái)。
該技術(shù)目前需要搭配yedf/dtm事務(wù)管理器,目前SDK已經(jīng)提供給Go、Python語(yǔ)言的開(kāi)發(fā)者。其他語(yǔ)言的sdk正在規劃中。對于其他的分布式事務(wù)框架,只要提供了合適的分布式事務(wù)信息,能夠按照上述原理,快速實(shí)現該技術(shù)。yedf/dtm支持了TCC、XA、SAGA、事務(wù)消息、最大努力通知(使用事務(wù)消息實(shí)現),提供了HTTP、gRPC協(xié)議支持,非常容易接入。
免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng )、來(lái)自本網(wǎng)站內容采集于網(wǎng)絡(luò )互聯(lián)網(wǎng)轉載等其它媒體和分享為主,內容觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如侵犯了原作者的版權,請告知一經(jīng)查實(shí),將立刻刪除涉嫌侵權內容,聯(lián)系我們QQ:712375056,同時(shí)歡迎投稿傳遞力量。
Copyright ? 2009-2022 56dr.com. All Rights Reserved. 特網(wǎng)科技 特網(wǎng)云 版權所有 特網(wǎng)科技 粵ICP備16109289號
域名注冊服務(wù)機構:阿里云計算有限公司(萬(wàn)網(wǎng)) 域名服務(wù)機構:煙臺帝思普網(wǎng)絡(luò )科技有限公司(DNSPod) CDN服務(wù):阿里云計算有限公司 百度云 中國互聯(lián)網(wǎng)舉報中心 增值電信業(yè)務(wù)經(jīng)營(yíng)許可證B2
建議您使用Chrome、Firefox、Edge、IE10及以上版本和360等主流瀏覽器瀏覽本網(wǎng)站