事務(wù)是數(shù)據(jù)庫(kù)系統(tǒng)中非常有趣也非常重要的概念,它是數(shù)據(jù)庫(kù)管理系統(tǒng)執(zhí)行過(guò)程中的一個(gè)邏輯單元,它能夠保證一個(gè)事務(wù)中的所有操作要么全部執(zhí)行,要么全不執(zhí)行;在 SOA 與微服務(wù)架構(gòu)大行其道的今天,在分布式的多個(gè)服務(wù)中保證業(yè)務(wù)的一致性就需要我們實(shí)現(xiàn)分布式事務(wù)。
創(chuàng)新互聯(lián)公司專注于德宏州企業(yè)網(wǎng)站建設(shè),響應(yīng)式網(wǎng)站建設(shè),成都商城網(wǎng)站開發(fā)。德宏州網(wǎng)站建設(shè)公司,為德宏州等地區(qū)提供建站服務(wù)。全流程按需定制制作,專業(yè)設(shè)計(jì),全程項(xiàng)目跟蹤,創(chuàng)新互聯(lián)公司專業(yè)和態(tài)度為您提供的服務(wù)
在這篇文章中,我們將介紹 事務(wù)的實(shí)現(xiàn)原理 、分布式事務(wù)的理論基礎(chǔ)以及實(shí)現(xiàn)原理。
事務(wù)
在文章的開頭,我們已經(jīng)說(shuō)過(guò)事務(wù)是數(shù)據(jù)庫(kù)管理系統(tǒng)執(zhí)行過(guò)程中的一個(gè)邏輯單位,它能保證一組數(shù)據(jù)庫(kù)操作要么全部執(zhí)行,要么全不執(zhí)行,我們能夠通過(guò)事務(wù)將數(shù)據(jù)庫(kù)從一個(gè)狀態(tài)遷移到另一個(gè)狀態(tài),在每一個(gè)狀態(tài)中,數(shù)據(jù)庫(kù)中的數(shù)據(jù)都保持一致性。
數(shù)據(jù)庫(kù)事務(wù)擁有四個(gè)特性,原子性(Atomicity)、一致性(Consistency)、隔離性(Isolation)和持久性(Durability):
我們經(jīng)常將這上述的四大特性簡(jiǎn)寫為 ACID,而數(shù)據(jù)庫(kù)事務(wù)的實(shí)現(xiàn)原理其實(shí)也就是實(shí)現(xiàn)這四大特性的原理。
實(shí)現(xiàn)原理
在之前的文章 『淺入深出』MySQL 中事務(wù)的實(shí)現(xiàn) 中其實(shí)已經(jīng)對(duì)如何實(shí)現(xiàn)事務(wù)的 ACID 這幾個(gè)基本屬性給出了比較詳細(xì)的介紹和分析,在這里就簡(jiǎn)單介紹幾個(gè)比較重要的實(shí)現(xiàn)細(xì)節(jié),關(guān)于展開的內(nèi)容,可以閱讀上述文章。
事務(wù)日志
為了實(shí)現(xiàn)確保事務(wù)能在執(zhí)行的任意過(guò)程中回滾(原子性)并且提交的事務(wù)會(huì)永久保存在數(shù)據(jù)庫(kù)中,我們會(huì)使用事務(wù)日志來(lái)存儲(chǔ)事務(wù)執(zhí)行過(guò)程中的數(shù)據(jù)庫(kù)的變動(dòng),每一條事務(wù)日志中都包含事務(wù)的 ID、當(dāng)前被修改的元素、變動(dòng)前以及變動(dòng)后的值。
當(dāng)我們有以上的事務(wù)日志之后,一旦需要對(duì)事務(wù)進(jìn)行回滾就非常容易了,數(shù)據(jù)庫(kù)會(huì)根據(jù)上述日志生成一個(gè)相反的操作恢復(fù)事務(wù)發(fā)生之前的狀態(tài);事務(wù)日志除了能夠?qū)κ聞?wù)進(jìn)行回滾保證原子性之外,還能夠?qū)崿F(xiàn)持久性,當(dāng)一個(gè)事務(wù)常食對(duì)數(shù)據(jù)庫(kù)進(jìn)行修改時(shí),它其實(shí)會(huì)先生成一條日志并刷新到磁盤上,寫日志的操作由于是追加的所以非???,在這之后才會(huì)向數(shù)據(jù)庫(kù)中寫入或者更新對(duì)應(yīng)的記錄。
在 MySQL 最常見的存儲(chǔ)引擎 InnoDB 中,事務(wù)日志其實(shí)有兩種,一種是回滾日志(undo log),另一種是重做日志(redo log),其中前者保證事務(wù)的原子性,后者保證事務(wù)的持久性,兩者可以統(tǒng)稱為事務(wù)日志。
并發(fā)控制
數(shù)據(jù)庫(kù)作為最關(guān)鍵的后端服務(wù),很難想象只能串行執(zhí)行每一個(gè)數(shù)據(jù)庫(kù)操作帶來(lái)的性能影響,然而在并發(fā)執(zhí)行 SQL 的過(guò)程中就可能無(wú)法保證數(shù)據(jù)庫(kù)對(duì)于隔離性的要求,歸根結(jié)底這就是一致性、隔離性與性能之間的權(quán)衡。
為了避免并發(fā)帶來(lái)的一致性問(wèn)題、滿足數(shù)據(jù)庫(kù)對(duì)于隔離性要求,數(shù)據(jù)庫(kù)系統(tǒng)往往都會(huì)使用并發(fā)控制機(jī)制盡可能地充分利用機(jī)器的效率,最常見的幾種并發(fā)控制機(jī)制就是鎖、時(shí)間戳和 MVCC:
作為悲觀并發(fā)控制機(jī)制,鎖使用在更新資源之前對(duì)資源進(jìn)行鎖定的方式保證多個(gè)數(shù)據(jù)庫(kù)的會(huì)話同時(shí)修改某一行記錄時(shí)不會(huì)出現(xiàn)脫離預(yù)期的行為,而時(shí)間戳這種方式在每次提交時(shí)對(duì)資源是否被改變進(jìn)行檢查。
在 淺談數(shù)據(jù)庫(kù)并發(fā)控制 - 鎖和 MVCC 中,作者介紹過(guò)幾種不同的并發(fā)控制機(jī)制的實(shí)現(xiàn)原理,想要了解更多相關(guān)的內(nèi)容的可以閱讀這篇文章。
分布式事務(wù)
從廣義上來(lái)看,分布式事務(wù)其實(shí)也是事務(wù),只是由于業(yè)務(wù)上的定義以及微服務(wù)架構(gòu)設(shè)計(jì)的問(wèn)題,所以需要在多個(gè)服務(wù)之間保證業(yè)務(wù)的事務(wù)性,也就是 ACID 四個(gè)特性;從單機(jī)的數(shù)據(jù)庫(kù)事務(wù)變成分布式事務(wù)時(shí),原有單機(jī)中相對(duì)可靠的方法調(diào)用以及進(jìn)程間通信方式已經(jīng)沒(méi)有辦法使用,同時(shí)由于網(wǎng)絡(luò)通信經(jīng)常是不穩(wěn)定的,所以服務(wù)之間信息的傳遞會(huì)出現(xiàn)障礙。
模塊(或服務(wù))之間通信方式的改變是造成分布式事務(wù)復(fù)雜的最主要原因,在同一個(gè)事務(wù)之間的執(zhí)行多段代碼會(huì)因?yàn)榫W(wǎng)絡(luò)的不穩(wěn)定造成各種奇怪的問(wèn)題,當(dāng)我們通過(guò)網(wǎng)絡(luò)請(qǐng)求其他服務(wù)的接口時(shí),往往會(huì)得到三種結(jié)果:正確、失敗和超時(shí),無(wú)論是成功還是失敗,我們都能得到唯一確定的結(jié)果,超時(shí)代表請(qǐng)求的發(fā)起者不能確定接受者是否成功處理了請(qǐng)求,這也是造成諸多問(wèn)題的誘因。
系統(tǒng)之間的通信可靠性從單一系統(tǒng)中的可靠變成了微服務(wù)架構(gòu)之間的不可靠,分布式事務(wù)其實(shí)就是在不可靠的通信下實(shí)現(xiàn)事務(wù)的特性。無(wú)論是事務(wù)還是分布式事務(wù)實(shí)現(xiàn)原子性都無(wú)法避免對(duì)持久存儲(chǔ)的依賴,事務(wù)使用磁盤上的日志記錄執(zhí)行的過(guò)程以及上下文,這樣無(wú)論是需要回滾還是補(bǔ)償都可以通過(guò)日志追溯,而分布式事務(wù)也會(huì)依賴數(shù)據(jù)庫(kù)、Zookeeper 或者 ETCD 等服務(wù)追蹤事務(wù)的執(zhí)行過(guò)程,總而言之,各種形式的日志是保證事務(wù)幾大特性的重要手段。
2PC 與 3PC
兩階段提交是一種使分布式系統(tǒng)中所有節(jié)點(diǎn)在進(jìn)行事務(wù)提交時(shí)保持一致性而設(shè)計(jì)的一種協(xié)議;在一個(gè)分布式系統(tǒng)中,所有的節(jié)點(diǎn)雖然都可以知道自己執(zhí)行操作后的狀態(tài),但是無(wú)法知道其他節(jié)點(diǎn)執(zhí)行操作的狀態(tài),在一個(gè)事務(wù)跨越多個(gè)系統(tǒng)時(shí),就需要引入一個(gè)作為協(xié)調(diào)者的組件來(lái)統(tǒng)一掌控全部的節(jié)點(diǎn)并指示這些節(jié)點(diǎn)是否把操作結(jié)果進(jìn)行真正的提交,想要在分布式系統(tǒng)中實(shí)現(xiàn)一致性的其他協(xié)議都是在兩階段提交的基礎(chǔ)上做的改進(jìn)。
兩階段提交的執(zhí)行過(guò)程就跟它的名字一樣分為兩個(gè)階段,投票階段和提交階段,在投票階段中,協(xié)調(diào)者(Coordinator)會(huì)向事務(wù)的參與者(Cohort)詢問(wèn)是否可以執(zhí)行操作的請(qǐng)求,并等待其他參與者的響應(yīng),參與者會(huì)執(zhí)行相對(duì)應(yīng)的事務(wù)操作并記錄重做和回滾日志,所有執(zhí)行成功的參與者會(huì)向協(xié)調(diào)者發(fā)送 AGREEMENT 或者 ABORT 表示執(zhí)行操作的結(jié)果。
當(dāng)所有的參與者都返回了確定的結(jié)果(同意或者終止)時(shí),兩階段提交就進(jìn)入了提交階段,協(xié)調(diào)者會(huì)根據(jù)投票階段的返回情況向所有的參與者發(fā)送提交或者回滾的指令。
當(dāng)事務(wù)的所有參與者都決定提交事務(wù)時(shí),協(xié)調(diào)者會(huì)向參與者發(fā)送 COMMIT 請(qǐng)求,參與者在完成操作并釋放資源之后向協(xié)調(diào)者返回完成消息,協(xié)調(diào)者在收到所有參與者的完成消息時(shí)會(huì)結(jié)束整個(gè)事務(wù);與之相反,當(dāng)有參與者決定 ABORT 當(dāng)前事務(wù)時(shí),協(xié)調(diào)者會(huì)向事務(wù)的參與者發(fā)送回滾請(qǐng)求,參與者會(huì)根據(jù)之前執(zhí)行操作時(shí)的回滾日志對(duì)操作進(jìn)行回滾并向協(xié)調(diào)者發(fā)送完成的消息,在提交階段,無(wú)論當(dāng)前事務(wù)被提交還是回滾,所有的資源都會(huì)被釋放并且事務(wù)也一定會(huì)結(jié)束。
兩階段提交協(xié)議是一個(gè)阻塞協(xié)議,也就是說(shuō)在兩階段提交的執(zhí)行過(guò)程中,除此之外,如果事務(wù)的執(zhí)行過(guò)程中協(xié)調(diào)者永久宕機(jī),事務(wù)的一部分參與者將永遠(yuǎn)無(wú)法完成事務(wù),它們會(huì)等待協(xié)調(diào)者發(fā)送 COMMIT 或者 ROLLBACK 消息,甚至?xí)霈F(xiàn)多個(gè)參與者狀態(tài)不一致的問(wèn)題。
3PC
為了解決兩階段提交在協(xié)議的一些問(wèn)題,三階段提交引入了超時(shí)機(jī)制和準(zhǔn)備階段,如果協(xié)調(diào)者或者參與者在規(guī)定的之間內(nèi)沒(méi)有接受到來(lái)自其他節(jié)點(diǎn)的響應(yīng),就會(huì)根據(jù)當(dāng)前的狀態(tài)選擇提交或者終止整個(gè)事務(wù),準(zhǔn)備階段的引入其實(shí)讓事務(wù)的參與者有了除回滾之外的其他選擇。
當(dāng)參與者向協(xié)調(diào)者發(fā)送 ACK 后,如果長(zhǎng)時(shí)間沒(méi)有得到協(xié)調(diào)者的響應(yīng),在默認(rèn)情況下,參與者會(huì)自動(dòng)將超時(shí)的事務(wù)進(jìn)行提交,不會(huì)像兩階段提交中被阻塞?。簧鲜龅膱D片非常清楚地說(shuō)明了在不同階段,協(xié)調(diào)者或者參與者的超時(shí)會(huì)造成什么樣的行為。
XA 事務(wù)
MySQL 的 InnoDB 引擎其實(shí)能夠支持分布式事務(wù),也就是我們經(jīng)常說(shuō)的 XA 事務(wù);XA 事務(wù)就是用了我們?cè)谏弦还?jié)中提到的兩階段提交協(xié)議實(shí)現(xiàn)分布式事務(wù),其中事務(wù)管理器為協(xié)調(diào)者,而資源管理器就是分布式事務(wù)的參與者。
到這里,其實(shí)我們已經(jīng)能夠清晰地知道 MySQL 中的 XA 事務(wù)是如何實(shí)現(xiàn)的:
資源管理器提供了訪問(wèn)事務(wù)資源的能力,數(shù)據(jù)庫(kù)就是一種常見的資源管理器,它能夠提交或者回滾其管理的事務(wù);
事務(wù)管理器協(xié)調(diào)整個(gè)分布式事務(wù)的各個(gè)部分,它與多個(gè)資源管理器通信,分別處理他們管理的事務(wù),這些事務(wù)都是整體事務(wù)的一個(gè)分支。
正如兩階段提交協(xié)議中定義的,MySQL 提供的 XA 接口可以非常方便地實(shí)現(xiàn)協(xié)議中的投票和提交階段,我們可以通過(guò)一下的流程圖簡(jiǎn)單理解一下 MySQL XA 的接口是如何使用的:
XA 確實(shí)能夠保證較強(qiáng)的一致性,但是在 MySQL XA 的執(zhí)行過(guò)程中會(huì)對(duì)相應(yīng)的資源加鎖,阻塞其他事務(wù)對(duì)該資源的訪問(wèn),如果事務(wù)長(zhǎng)時(shí)間沒(méi)有 COMMIT 或者 ROLLBACK,其實(shí)會(huì)對(duì)數(shù)據(jù)庫(kù)造成比較嚴(yán)重的影響。
Saga
兩階段提交其實(shí)可以保證事務(wù)的強(qiáng)一致性,但是在很多業(yè)務(wù)場(chǎng)景下,我們其實(shí)只需要保證業(yè)務(wù)的最終一致性,在一定的時(shí)間窗口內(nèi),多個(gè)系統(tǒng)中的數(shù)據(jù)不一致是可以接受的,在過(guò)了時(shí)間窗口之后,所有系統(tǒng)都會(huì)返回一致的結(jié)果。
Saga 其實(shí)就一種簡(jiǎn)化的分布式事務(wù)解決方案,它將一系列的分布式操作轉(zhuǎn)化成了一系列的本地事務(wù),在每一個(gè)本地事務(wù)中我們都會(huì)更新數(shù)據(jù)庫(kù)并且向集群中的其他服務(wù)發(fā)送一條的新的消息來(lái)觸發(fā)下一個(gè)本地的事務(wù);一旦本地的事務(wù)因?yàn)檫`反了業(yè)務(wù)邏輯而失敗,那么就會(huì)立刻觸發(fā)一系列的回滾操作來(lái)撤回之前本地事務(wù)造成的副作用。
LLT
相比于本地的數(shù)據(jù)庫(kù)事務(wù)來(lái)說(shuō),長(zhǎng)事務(wù)(Long Lived Transaction)會(huì)對(duì)一些數(shù)據(jù)庫(kù)資源持有相對(duì)較長(zhǎng)的一段時(shí)間,這會(huì)嚴(yán)重地影響其他正常數(shù)據(jù)庫(kù)事務(wù)的執(zhí)行,為了解決這一問(wèn)題,Hector Garcia-Molina 和 Kenneth Salem 在 1987 發(fā)布了論文 Sagas 用于解決這一問(wèn)題。
如果一個(gè) LLT 能夠被改寫成一系列的相互交錯(cuò)重疊的多個(gè)數(shù)據(jù)庫(kù)事務(wù),那么這個(gè) LLT 就是一個(gè) Saga;數(shù)據(jù)庫(kù)系統(tǒng)能夠保證 Saga 中一系列的事務(wù)要么全部成功執(zhí)行、要么它們的補(bǔ)償事務(wù)能夠回滾全部的副作用,保證整個(gè)分布式事務(wù)的最終一致性。Saga 的概念和它的實(shí)現(xiàn)都是非常簡(jiǎn)單的,但是它卻能夠有很大的潛力增加整個(gè)系統(tǒng)的處理能力。
事務(wù)越長(zhǎng)并且越復(fù)雜,那么這個(gè)事務(wù)由于異常而被回滾以及死鎖的可能性就會(huì)逐漸增加,Saga 會(huì)將一個(gè) LLT 分解成多個(gè)短事務(wù),能夠非常明顯地降低事務(wù)被回滾的風(fēng)險(xiǎn)。
協(xié)同與編排
當(dāng)我們使用 Saga 模式開發(fā)分布式事務(wù)時(shí),有兩種協(xié)調(diào)不同服務(wù)的方式,一種是協(xié)同(Choreography),另一種是編排(Orchestration):
如果對(duì)于一個(gè)分布式事務(wù),我們采用協(xié)同的方式進(jìn)行開發(fā),每一個(gè)本地的事務(wù)都會(huì)觸發(fā)一個(gè)其他服務(wù)中的本地事務(wù)的執(zhí)行,也就是說(shuō)事務(wù)的執(zhí)行過(guò)程是一個(gè)流的形式進(jìn)行的:
當(dāng)我們選擇使用協(xié)同的方式處理事務(wù)時(shí),服務(wù)之間的通信其實(shí)就是通過(guò)事件進(jìn)行的,每一個(gè)本的事務(wù)最終都會(huì)向服務(wù)的下游發(fā)送一個(gè)新的事件,既可以是消息隊(duì)列中的消息,也可以是 RPC 的請(qǐng)求,只是下游提供的接口需要保證冪等和重入。
除此之外,通過(guò)協(xié)同方式創(chuàng)建的分布式事務(wù)其實(shí)并沒(méi)有明顯的中心化節(jié)點(diǎn),多個(gè)服務(wù)參與者之間的交互協(xié)議要從全局來(lái)定義,每個(gè)服務(wù)能夠處理以及發(fā)送的事件和接口都需要進(jìn)行比較嚴(yán)謹(jǐn)?shù)脑O(shè)計(jì),盡可能提供抽象程度高的事件或者接口,這樣各個(gè)服務(wù)才能實(shí)現(xiàn)自治并重用已有的代碼和邏輯。
如果我們不想使用協(xié)同的方式對(duì)分布式事務(wù)進(jìn)行處理,那么也可以選擇編排的方式實(shí)現(xiàn)分布式事務(wù),編排的方式引入了中心化的協(xié)調(diào)器節(jié)點(diǎn),我們通過(guò)一個(gè) Saga 對(duì)象來(lái)追蹤所有的子任務(wù)的調(diào)用情況,根據(jù)任務(wù)的調(diào)用情況決定是否需要調(diào)用對(duì)應(yīng)的補(bǔ)償方案,并在網(wǎng)絡(luò)請(qǐng)求出現(xiàn)超時(shí)時(shí)進(jìn)行重試:
在這里我們就引入了一個(gè)中心化的『協(xié)調(diào)器』,它會(huì)保存當(dāng)前分布式事務(wù)進(jìn)行到底的狀態(tài),并根據(jù)情況對(duì)事務(wù)進(jìn)行回滾或者提交操作,在服務(wù)編排的過(guò)程中,我們是從協(xié)調(diào)者本身觸發(fā)考慮整個(gè)事務(wù)的執(zhí)行過(guò)程的,相對(duì)于協(xié)同的方式,編排實(shí)現(xiàn)的過(guò)程相對(duì)來(lái)說(shuō)更為簡(jiǎn)單。
協(xié)同與編排其實(shí)是兩種思路截然相反的模式,前者強(qiáng)調(diào)各個(gè)服務(wù)的自治與去中心化,后者需要一個(gè)中心化的組件對(duì)事務(wù)執(zhí)行的過(guò)程進(jìn)行統(tǒng)一的管理,兩者的優(yōu)缺點(diǎn)其實(shí)就是中心化與去中心化的優(yōu)缺點(diǎn),中心化的方案往往都會(huì)造就一個(gè)『上帝服務(wù)』,其中包含了非常多組織與集成其他節(jié)點(diǎn)的工作,也會(huì)有單點(diǎn)故障的問(wèn)題,而去中心化的方案就會(huì)帶來(lái)管理以及調(diào)試上的不便,當(dāng)我們需要追蹤一個(gè)業(yè)務(wù)的執(zhí)行過(guò)程時(shí)就需要跨越多個(gè)服務(wù)進(jìn)行,增加了維護(hù)的成本。
當(dāng)我們選擇使用 Saga 對(duì)分布式事務(wù)進(jìn)行開發(fā)時(shí),會(huì)對(duì)分布式事務(wù)的參與者有一定的約束,每一個(gè)事務(wù)的參與者都需要保證:
提供接口和補(bǔ)償副作用的接口;
接口支持重入并通過(guò)全局唯一的 ID 保證冪等;
這樣我們就能夠保證一個(gè)長(zhǎng)事務(wù)能夠在網(wǎng)絡(luò)通信發(fā)生超時(shí)時(shí)進(jìn)行重試,同時(shí)在需要對(duì)事務(wù)進(jìn)行回滾時(shí)調(diào)用回滾接口達(dá)到我們的目的。
Saga 這種模式其實(shí)完全放棄了同時(shí)滿足事務(wù)四大基本特性 ACID 的想法,而是選擇降低實(shí)現(xiàn)分布式事務(wù)的難度并減少資源同步以及鎖定帶來(lái)的問(wèn)題,選擇實(shí)現(xiàn) BASE(Basic Availability, Soft, Eventual consistency) 事務(wù),達(dá)到業(yè)務(wù)上的基本可用以及最終一致性,在絕大多數(shù)的業(yè)務(wù)場(chǎng)景中,實(shí)現(xiàn)最終一致性就能夠基本滿足業(yè)務(wù)的全部需求,極端場(chǎng)景下還是應(yīng)該選擇兩階段提交或者干脆放棄分布式事務(wù)這種易錯(cuò)的實(shí)現(xiàn)方式,轉(zhuǎn)而使用單機(jī)中的數(shù)據(jù)庫(kù)事務(wù)來(lái)解決。
分布式事務(wù)帶來(lái)復(fù)雜度的原因其實(shí)就是由于各個(gè)模塊之間的通信不穩(wěn)定,當(dāng)我們發(fā)出一個(gè)網(wǎng)絡(luò)請(qǐng)求時(shí),可能的返回結(jié)果是成功、失敗或者超時(shí)。
網(wǎng)絡(luò)無(wú)論是返回成功還是失敗其實(shí)都是一個(gè)確定的結(jié)果,當(dāng)網(wǎng)絡(luò)請(qǐng)求超時(shí)的時(shí)候其實(shí)非常不好處理,在這時(shí)調(diào)用方并不能確定這一次請(qǐng)求是否送達(dá)而且不會(huì)知道請(qǐng)求的結(jié)果,但是消息服務(wù)可以保證某條信息一定會(huì)送達(dá)到調(diào)用方;大多數(shù)消息服務(wù)都會(huì)提供兩種不同的 QoS,也就是服務(wù)的等級(jí)。
最常見的兩種服務(wù)等級(jí)就是 At-Most-Once 和 At-Least-Once,前者能夠保證發(fā)送方不對(duì)接收方是否能收到消息作保證,消息要么會(huì)被投遞一次,要么不會(huì)被投遞,這其實(shí)跟一次普通的網(wǎng)絡(luò)請(qǐng)求沒(méi)有太多的區(qū)別;At-Least-Once 能夠解決消息投遞失敗的問(wèn)題,它要求發(fā)送者檢查投遞的結(jié)果,并在失敗或者超時(shí)時(shí)重新對(duì)消息進(jìn)行投遞,發(fā)送者會(huì)持續(xù)對(duì)消息進(jìn)行推送,直到接受者確認(rèn)消息已經(jīng)被收到,相比于 At-Most-Once,At-Least-Once 因?yàn)槟軌虼_保消息的投遞會(huì)被更多人使用。
除了這兩種常見的服務(wù)等級(jí)之外,還有另一種服務(wù)等級(jí),也就是 Exactly-Once,這種服務(wù)等級(jí)不僅對(duì)發(fā)送者提出了要求,還對(duì)消費(fèi)者提出了要求,它需要接受者對(duì)接收到的所有消息進(jìn)行去重,發(fā)送者和接受者一方對(duì)消息進(jìn)行重試,另一方對(duì)消息進(jìn)行去重,兩者分別部署在不同的節(jié)點(diǎn)上,這樣對(duì)于各個(gè)節(jié)點(diǎn)上的服務(wù)來(lái)說(shuō),它們之間的通信就是 Exactly-Once 的,但是需要注意的是,Exacly-Once 一定需要接收方的參與。
我們可以通過(guò)實(shí)現(xiàn) AMQP 協(xié)議的消息隊(duì)列來(lái)實(shí)現(xiàn)分布式事務(wù),在協(xié)議的標(biāo)準(zhǔn)中定義了 tx_select、tx_commit 和 tx_rollback 三個(gè)事務(wù)相關(guān)的接口,其中 tx_select 能夠開啟事務(wù),tx_commit 和 tx_rollback 分別能夠提交或者回滾事務(wù)。
使用消息服務(wù)實(shí)現(xiàn)分布式事務(wù)在底層的原理上與其他的方法沒(méi)有太多的差別,只是消息服務(wù)能夠幫助我們實(shí)現(xiàn)的消息的持久化以及重試等功能,能夠?yàn)槲覀兲峁┮粋€(gè)比較合理的 API 接口,方便開發(fā)者使用。
總結(jié)
分布式事務(wù)的實(shí)現(xiàn)方式是分布式系統(tǒng)中非常重要的一個(gè)問(wèn)題,在微服務(wù)架構(gòu)和 SOA 大行其道的今天,掌握分布式事務(wù)的原理和使用方式已經(jīng)是作為后端開發(fā)者理所應(yīng)當(dāng)掌握的技能,從實(shí)現(xiàn) ACID 事務(wù)的 2PC 與 3PC 到實(shí)現(xiàn) BASE 補(bǔ)償式事務(wù)的 Saga,再到最后通過(guò)事務(wù)消息的方式異步地保證消息最終一定會(huì)被消費(fèi)成功,我們?yōu)榱嗽黾酉到y(tǒng)的吞吐量以及可用性逐漸降低了系統(tǒng)對(duì)一致性的要求。
在業(yè)務(wù)沒(méi)有對(duì)一致性有那么強(qiáng)的需求時(shí),作者一般會(huì)使用 Saga 協(xié)議對(duì)分布式事務(wù)進(jìn)行設(shè)計(jì)和開發(fā),而在實(shí)際工作中,需要強(qiáng)一致性事務(wù)的業(yè)務(wù)場(chǎng)景幾乎沒(méi)有,我們都可以實(shí)現(xiàn)最終一致性,在發(fā)生腦裂或者不一致問(wèn)題時(shí)通過(guò)補(bǔ)償?shù)姆绞竭M(jìn)行解決,這就能解決幾乎全部的問(wèn)題。