優(yōu)質(zhì)文章,第一時間送達(dá) 隨著業(yè)務(wù)的快速發(fā)展、業(yè)務(wù)復(fù)雜度越來越高,幾乎每個公司的系統(tǒng)都會從單體走向分布式,特別是轉(zhuǎn)向微服務(wù)架構(gòu)。隨之而來就必然遇到分布式事務(wù)這個難題,這篇文章總結(jié)了分布式事務(wù)最經(jīng)典的解決方案,分享給大家。 基礎(chǔ)理論在講解具體方案之前,我們先了解一下分布式事務(wù)所涉及到的基礎(chǔ)理論知識。 我們拿轉(zhuǎn)賬作為例子,A需要轉(zhuǎn)100元給B,那么需要給A的余額-100元,給B的余額+100元,整個轉(zhuǎn)賬要保證,A-100和B+100同時成功,或者同時失敗。看看在各種場景下,是如何解決這個問題的。 事務(wù)把多條語句作為一個整體進行操作的功能,被稱為數(shù)據(jù)庫事務(wù)。數(shù)據(jù)庫事務(wù)可以確保該事務(wù)范圍內(nèi)的所有操作都可以全部成功或者全部失敗。 事務(wù)具有 4 個屬性:原子性、一致性、隔離性、持久性。這四個屬性通常稱為 ACID 特性。
分布式事務(wù)銀行跨行轉(zhuǎn)賬業(yè)務(wù)是一個典型分布式事務(wù)場景,假設(shè)A需要跨行轉(zhuǎn)賬給B,那么就涉及兩個銀行的數(shù)據(jù),無法通過一個數(shù)據(jù)庫的本地事務(wù)保證轉(zhuǎn)賬的ACID,只能夠通過分布式事務(wù)來解決。 分布式事務(wù)就是指事務(wù)的發(fā)起者、資源及資源管理器和事務(wù)協(xié)調(diào)者分別位于分布式系統(tǒng)的不同節(jié)點之上。在上述轉(zhuǎn)賬的業(yè)務(wù)中,用戶A-100操作和用戶B+100操作不是位于同一個節(jié)點上。本質(zhì)上來說,分布式事務(wù)就是為了保證在分布式場景下,數(shù)據(jù)操作的正確執(zhí)行。 分布式事務(wù)在分布式環(huán)境下,為了滿足可用性、性能與降級服務(wù)的需要,降低一致性與隔離性的要求,一方面遵循 BASE 理論(BASE相關(guān)理論,涉及內(nèi)容非常多,感興趣的同學(xué),可以參考BASE理論): 基本業(yè)務(wù)可用性(Basic Availability) 原子性:嚴(yán)格遵循 分布式事務(wù)的解決方案兩階段提交/XAXA是由X/Open組織提出的分布式事務(wù)的規(guī)范,XA規(guī)范主要定義了(全局)事務(wù)管理器(TM)和(局部)資源管理器(RM)之間的接口。本地的數(shù)據(jù)庫如mysql在XA中扮演的是RM角色 XA一共分為兩階段: 第一階段(prepare):即所有的參與者RM準(zhǔn)備執(zhí)行事務(wù)并鎖住需要的資源。參與者ready時,向TM報告已準(zhǔn)備就緒。 XA 事務(wù)由一個或多個資源管理器(RM)、一個事務(wù)管理器(TM)和一個應(yīng)用程序(ApplicationProgram)組成。 把上面的轉(zhuǎn)賬作為例子,一個成功完成的XA事務(wù)時序圖如下: 如果有任何一個參與者prepare失敗,那么TM會通知所有完成prepare的參與者進行回滾。 XA事務(wù)的特點是:
如果讀者想要進一步研究XA,go語言可參考DTM,java語言可參考seata SAGASaga是這一篇數(shù)據(jù)庫論文saga提到的一個方案。其核心思想是將長事務(wù)拆分為多個本地短事務(wù),由Saga事務(wù)協(xié)調(diào)器協(xié)調(diào),如果正常結(jié)束那就正常完成,如果某個步驟失敗,則根據(jù)相反順序一次調(diào)用補償操作。 把上面的轉(zhuǎn)賬作為例子,一個成功完成的SAGA事務(wù)時序圖如下:
論文里面的SAGA內(nèi)容較多,包括兩種恢復(fù)策略,包括分支事務(wù)并發(fā)執(zhí)行,我們這里的討論,僅包括最簡單的SAGA SAGA適用的場景較多,長事務(wù)適用,對中間結(jié)果不敏感的業(yè)務(wù)場景適用 如果讀者想要進一步研究SAGA,go語言可參考DTM,java語言可參考seata TCC關(guān)于 TCC(Try-Confirm-Cancel)的概念,最早是由 Pat Helland 于 2007 年發(fā)表的一篇名為《Life beyond Distributed Transactions:an Apostate’s Opinion》的論文提出。 TCC分為3個階段
把上面的轉(zhuǎn)賬作為例子,通常會在Try里面凍結(jié)金額,但不扣款,Confirm里面扣款,Cancel里面解凍金額,一個成功完成的TCC事務(wù)時序圖如下: TCC特點如下:
如果讀者想要進一步研究TCC,go語言可參考DTM,java語言可參考seata 本地消息表本地消息表這個方案最初是 ebay 架構(gòu)師 Dan Pritchett 在 2008 年發(fā)表給 ACM 的文章。設(shè)計核心是將需要分布式處理的任務(wù)通過消息的方式來異步確保執(zhí)行。 大致流程如下:
容錯機制:
本地消息表的特點:
適用于可異步執(zhí)行的業(yè)務(wù),且后續(xù)操作無需回滾的業(yè)務(wù) 事務(wù)消息在上述的本地消息表方案中,生產(chǎn)者需要額外創(chuàng)建消息表,還需要對本地消息表進行輪詢,業(yè)務(wù)負(fù)擔(dān)較重。阿里開源的RocketMQ 4.3之后的版本正式支持事務(wù)消息,該事務(wù)消息本質(zhì)上是把本地消息表放到RocketMQ上,解決生產(chǎn)端的消息發(fā)送與本地事務(wù)執(zhí)行的原子性問題。 事務(wù)消息發(fā)送及提交:
正常發(fā)送的流程圖如下: 補償流程: 對沒有Commit/Rollback的事務(wù)消息(pending狀態(tài)的消息),從服務(wù)端發(fā)起一次“回查” 事務(wù)消息特點如下:
適用于可異步執(zhí)行的業(yè)務(wù),且后續(xù)操作無需回滾的業(yè)務(wù) 如果讀者想要進一步研究事務(wù)消息,可參考rocketmq,為了方便大家學(xué)習(xí)事務(wù)消息,DTM也提供了簡單實現(xiàn) 最大努力通知發(fā)起通知方通過一定的機制最大努力將業(yè)務(wù)處理結(jié)果通知到接收方。具體包括: 有一定的消息重復(fù)通知機制。因為接收通知方可能沒有接收到通知,此時要有一定的機制對消息重復(fù)通知。 可靠消息一致性,發(fā)起通知方需要保證將消息發(fā)出去,并且將消息發(fā)到接收通知方,消息的可靠性關(guān)鍵由發(fā)起通知方來保證。 最大努力通知,發(fā)起通知方盡最大的努力將業(yè)務(wù)處理結(jié)果通知為接收通知方,但是可能消息接收不到,此時需要接收通知方主動調(diào)用發(fā)起通知方的接口查詢業(yè)務(wù)處理結(jié)果,通知的可靠性關(guān)鍵在接收通知方。 解決方案上,最大努力通知需要:
最大努力通知適用于業(yè)務(wù)通知類型,例如微信交易的結(jié)果,就是通過最大努力通知方式通知各個商戶,既有回調(diào)通知,也有交易查詢接口 AT事務(wù)模式這是阿里開源項目seata中的一種事務(wù)模式,在螞蟻金服也被稱為FMT。優(yōu)點是該事務(wù)模式使用方式,類似XA模式,業(yè)務(wù)無需編寫各類補償操作,回滾由框架自動完成,缺點也類似AT,存在較長時間的鎖,不滿足高并發(fā)的場景。有興趣的同學(xué)可以參考seata-AT 異常處理在分布式事務(wù)的各個環(huán)節(jié)都有可能出現(xiàn)網(wǎng)絡(luò)以及業(yè)務(wù)故障等問題,這些問題需要分布式事務(wù)的業(yè)務(wù)方做到防空回滾,冪等,防懸掛三個特性。 異常情況下面以TCC事務(wù)說明這些異常情況: 空回滾: 在沒有調(diào)用 TCC 資源 Try 方法的情況下,調(diào)用了二階段的 Cancel 方法,Cancel 方法需要識別出這是一個空回滾,然后直接返回成功。 出現(xiàn)原因是當(dāng)一個分支事務(wù)所在服務(wù)宕機或網(wǎng)絡(luò)異常,分支事務(wù)調(diào)用記錄為失敗,這個時候其實是沒有執(zhí)行Try階段,當(dāng)故障恢復(fù)后,分布式事務(wù)進行回滾則會調(diào)用二階段的Cancel方法,從而形成空回滾。 冪等: 由于任何一個請求都可能出現(xiàn)網(wǎng)絡(luò)異常,出現(xiàn)重復(fù)請求,所以所有的分布式事務(wù)分支,都需要保證冪等性 懸掛: 懸掛就是對于一個分布式事務(wù),其二階段 Cancel 接口比 Try 接口先執(zhí)行。 出現(xiàn)原因是在 RPC 調(diào)用分支事務(wù)try時,先注冊分支事務(wù),再執(zhí)行RPC調(diào)用,如果此時 RPC 調(diào)用的網(wǎng)絡(luò)發(fā)生擁堵,RPC 超時以后,TM就會通知RM回滾該分布式事務(wù),可能回滾完成后,RPC 請求才到達(dá)參與者真正執(zhí)行。 下面看一個網(wǎng)絡(luò)異常的時序圖,更好的理解上述幾種問題 業(yè)務(wù)處理請求4的時候,Cancel在Try之前執(zhí)行,需要處理空回滾 面對上述復(fù)雜的網(wǎng)絡(luò)異常情況,目前看到各家建議的方案都是業(yè)務(wù)方通過唯一鍵,去查詢相關(guān)聯(lián)的操作是否已完成,如果已完成則直接返回成功。相關(guān)的判斷邏輯較復(fù)雜,易出錯,業(yè)務(wù)負(fù)擔(dān)重。 子事務(wù)屏障在項目DTM中,出現(xiàn)了一種子事務(wù)屏障技術(shù),使用該技術(shù),能夠達(dá)到這個效果,看示意圖: 所有這些請求,到了子事務(wù)屏障后:不正常的請求,會被過濾;正常請求,通過屏障。開發(fā)者使用子事務(wù)屏障之后,前面所說的各種異常全部被妥善處理,業(yè)務(wù)開發(fā)人員只需要關(guān)注實際的業(yè)務(wù)邏輯,負(fù)擔(dān)大大降低。 func ThroughBarrierCall(db *sql.DB, transInfo *TransInfo, busiCall BusiFunc) 業(yè)務(wù)開發(fā)人員,在busiCall里面編寫自己的相關(guān)邏輯,調(diào)用該函數(shù)。ThroughBarrierCall保證,在空回滾、懸掛等場景下,busiCall不會被調(diào)用;在業(yè)務(wù)被重復(fù)調(diào)用時,有冪等控制,保證只被提交一次。 子事務(wù)屏障會管理TCC、SAGA、XA、事務(wù)消息等,也可以擴展到其他領(lǐng)域 子事務(wù)屏障原理子事務(wù)屏障技術(shù)的原理是,在本地數(shù)據(jù)庫,建立分支事務(wù)狀態(tài)表sub_trans_barrier,唯一鍵為全局事務(wù)id-子事務(wù)id-子事務(wù)分支名稱(try|confirm|cancel)
在此機制下,解決了網(wǎng)絡(luò)異常相關(guān)的問題
對于SAGA事務(wù),也是類似的機制。 子事務(wù)屏障小結(jié)子事務(wù)屏障技術(shù),為DTM首創(chuàng),它的意義在于設(shè)計簡單易實現(xiàn)的算法,提供了簡單易用的接口,在首創(chuàng),它的意義在于設(shè)計簡單易實現(xiàn)的算法,提供了簡單易用的接口,在這兩項的幫助下,開發(fā)人員徹底的從網(wǎng)絡(luò)異常的處理中解放出來。 該技術(shù)目前需要搭配DTM事務(wù)管理器,目前SDK已經(jīng)提供給go語言的開發(fā)者。其他語言的sdk正在規(guī)劃中。對于其他的分布式事務(wù)框架,只要提供了合適的分布式事務(wù)信息,能夠按照上述原理,快速實現(xiàn)該技術(shù)。 總結(jié)本文介紹了分布式事務(wù)的一些基礎(chǔ)理論,并對常用的分布式事務(wù)方案進行了講解,在文章的后半部分還給出了事務(wù)異常的原因、分類以及優(yōu)雅的解決方案。 作者 | 葉東富 來源 | https://segmentfault.com/a/1190000040321750 |
|