<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          微服務(wù)架構(gòu)下分布式事務(wù)解決方案

          共 16645字,需瀏覽 34分鐘

           ·

          2021-02-05 21:39

          前言

          在微服務(wù)架構(gòu)中,隨著服務(wù)的逐步拆分,數(shù)據(jù)庫(kù)私有已經(jīng)成為共識(shí),這也導(dǎo)致所面臨的分布式事務(wù)問(wèn)題成為微服務(wù)落地過(guò)程中一個(gè)非常難以逾越的障礙,但是目前尚沒(méi)有一個(gè)完整通用的解決方案。

          其實(shí)不僅僅是在微服務(wù)架構(gòu)中,隨著用戶(hù)訪(fǎng)問(wèn)量的逐漸上漲,數(shù)據(jù)庫(kù)甚至是服務(wù)的分片、分區(qū)、水平拆分、垂直拆分已經(jīng)逐漸成為較為常用的提升瓶頸的解決方案,因此越來(lái)越多的原子操作變成了跨庫(kù)甚至是跨服務(wù)的事務(wù)操作。最終結(jié)果是在對(duì)高性能、高擴(kuò)展性、高可用性的追求的道路上,我們開(kāi)始逐漸放松對(duì)一致性的追求,但是在很多場(chǎng)景下,尤其是賬務(wù),電商等業(yè)務(wù)中,不可避免的存在著一致性問(wèn)題,使得我們不得不去探尋一種機(jī)制,用以在分布式環(huán)境中保證事務(wù)的一致性。

          分布式事務(wù)有多種主流形態(tài),包括:

          • 基于 2PC(兩階段提交)實(shí)現(xiàn)的分布式事務(wù)

          • 基于 3PC(三階段提交)實(shí)現(xiàn)的分布式事務(wù)

          • 基于 TCC(補(bǔ)償事務(wù))實(shí)現(xiàn)的分布式事務(wù)

          • 基于 Saga 實(shí)現(xiàn)的分布式事務(wù)

          • 基于可靠消息(事務(wù)消息)最終一致性實(shí)現(xiàn)的分布式事務(wù)

          • 基于本地消息(本地消息表)最終一致性實(shí)現(xiàn)的分布式事務(wù)

          接下來(lái),本文將對(duì)這些形態(tài)的分布式事務(wù)進(jìn)行剖析,然后講解一下如何根據(jù)業(yè)務(wù)選擇對(duì)應(yīng)的分布式事務(wù)形態(tài)。

          本地?cái)?shù)據(jù)庫(kù)事務(wù)

          數(shù)據(jù)庫(kù)事務(wù)(transaction)是訪(fǎng)問(wèn)并可能操作各種數(shù)據(jù)項(xiàng)的一個(gè)數(shù)據(jù)庫(kù)操作序列,這些操作要么全部執(zhí)行,要么全部不執(zhí)行,是一個(gè)不可分割的工作單位。事務(wù)由事務(wù)開(kāi)始與事務(wù)結(jié)束之間執(zhí)行的全部數(shù)據(jù)庫(kù)操作組成。關(guān)系型數(shù)據(jù)庫(kù)(例如:MySQL、SQL Server、Oracle 等)事務(wù)都有以下幾個(gè)特性:原子性(Atomicity)、一致性(Consistency)、隔離性(Isolation)和持久性(Durabilily),簡(jiǎn)稱(chēng)就是 ACID。


          名稱(chēng)描述
          AAtomicity(原子性)一個(gè)事務(wù)中的所有操作,要么全部完成,要么全部不完成,不會(huì)在中間某個(gè)環(huán)節(jié)結(jié)束。事務(wù)在執(zhí)行過(guò)程中發(fā)生錯(cuò)誤,會(huì)被回滾到事務(wù)開(kāi)始前的狀態(tài),就像這個(gè)事務(wù)從來(lái)沒(méi)有執(zhí)行過(guò)一樣。
          CConsistency(一致性)在事務(wù)開(kāi)始之前和事務(wù)結(jié)束以后,數(shù)據(jù)庫(kù)的完整性沒(méi)有被破壞。
          IIsolation(隔離性)數(shù)據(jù)庫(kù)允許多個(gè)并發(fā)事務(wù)同時(shí)對(duì)數(shù)據(jù)進(jìn)行讀寫(xiě)和修改的能力。隔離性可以防止多個(gè)事務(wù)并發(fā)執(zhí)行時(shí)由于交叉執(zhí)行而導(dǎo)致數(shù)據(jù)的不一致。
          DDurability(持久性)事務(wù)處理結(jié)束后,對(duì)數(shù)據(jù)的修改就是永久的,即便系統(tǒng)故障也不會(huì)丟失。

          本地?cái)?shù)據(jù)庫(kù)事務(wù)操作也比較簡(jiǎn)單:開(kāi)始一個(gè)事務(wù),改變(插入,刪除,更新)數(shù)據(jù),然后提交事務(wù)(如果有異常時(shí)回滾事務(wù))。MySQL 事務(wù)處理使用到 begin 開(kāi)始一個(gè)事務(wù),rollback 事務(wù)回滾,commit 事務(wù)確認(rèn)。這里,事務(wù)提交后,通過(guò) redo log 記錄變更,通過(guò) undo log 在失敗時(shí)進(jìn)行回滾,保證事務(wù)的原子性。

          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          17
          18
          19
          20
          21
          22
          23
          Connection con = null;
          try {
          // 工具類(lèi)得到 connection 對(duì)象
          con = JdbcUtils.getConnection();

          // 關(guān)閉自動(dòng)提交,開(kāi)啟事務(wù)
          con.setAutoCommit(false);

          // 增、刪、改 等操作
          ...

          // 成功操作后提交事務(wù)
          con.commit();
          con.close();
          } catch (Exception e) {
          try {
          // 如果有異常時(shí)回滾事務(wù)
          con.rollback();
          con.close();
          } catch (SQLException e1) {
          e1.printStackTrace();
          }
          }


          但隨著業(yè)務(wù)數(shù)據(jù)規(guī)模的快速發(fā)展,數(shù)據(jù)量越來(lái)越大,單庫(kù)單表逐漸成為瓶頸。所以我們對(duì)數(shù)據(jù)庫(kù)進(jìn)行了水平拆分,將原單庫(kù)單表拆分成數(shù)據(jù)庫(kù)分片。分庫(kù)分表之后,原來(lái)在一個(gè)數(shù)據(jù)庫(kù)上就能完成的寫(xiě)操作,可能就會(huì)跨多個(gè)數(shù)據(jù)庫(kù),這就產(chǎn)生了跨數(shù)據(jù)庫(kù)事務(wù)問(wèn)題。

          何時(shí)選擇本地?cái)?shù)據(jù)庫(kù)事務(wù)?

          在條件允許的情況下,我們應(yīng)該盡可能地使用單機(jī)事務(wù),因?yàn)閱螜C(jī)事務(wù)里,無(wú)需額外協(xié)調(diào)其他數(shù)據(jù)源,減少了網(wǎng)絡(luò)交互時(shí)間消耗以及協(xié)調(diào)時(shí)所需的存儲(chǔ) IO 消耗,在修改等量業(yè)務(wù)數(shù)據(jù)的情況下,單機(jī)事務(wù)將會(huì)有更高的性能。但單機(jī)數(shù)據(jù)庫(kù)由于業(yè)務(wù)邏輯解耦等因素進(jìn)行了數(shù)據(jù)庫(kù)垂直拆分或者由于單機(jī)數(shù)據(jù)庫(kù)性能壓力等因素進(jìn)行了數(shù)據(jù)庫(kù)水平拆分之后,數(shù)據(jù)分布于多個(gè)數(shù)據(jù)庫(kù),這時(shí)若需要對(duì)多個(gè)數(shù)據(jù)庫(kù)的數(shù)據(jù)進(jìn)行協(xié)調(diào)變更,則需要引入分布式事務(wù)。

          分布式事務(wù)理論

          微服務(wù)使得單體架構(gòu)擴(kuò)展為分布式架構(gòu),在擴(kuò)展的過(guò)程中,逐漸喪失了單體架構(gòu)中數(shù)據(jù)源單一,可以直接依賴(lài)于數(shù)據(jù)庫(kù)進(jìn)行事務(wù)操作的能力,而關(guān)系型數(shù)據(jù)庫(kù)中,提供了強(qiáng)大的事務(wù)處理能力,可以滿(mǎn)足 ACID(Atomicity,Consistency,Isolation,Durability)的特性,這種特性保證了數(shù)據(jù)操作的強(qiáng)一致性,這也是分布式環(huán)境中弱一致性以及最終一致性能夠得以實(shí)現(xiàn)的基礎(chǔ)。

          數(shù)據(jù)一致性分為三個(gè)種類(lèi)型:強(qiáng)一致性,弱一致性以及最終一致性。對(duì)于關(guān)系型數(shù)據(jù)庫(kù),要求更新過(guò)的數(shù)據(jù)能被后續(xù)的訪(fǎng)問(wèn)都能看到,這是強(qiáng)一致性。如果能容忍后續(xù)的部分或者全部訪(fǎng)問(wèn)不到,則是弱一致性。如果經(jīng)過(guò)一段時(shí)間后要求能訪(fǎng)問(wèn)到更新后的數(shù)據(jù),則是最終一致性。數(shù)據(jù)庫(kù)實(shí)現(xiàn)的就是強(qiáng)一致性,能夠保證在寫(xiě)入一份新的數(shù)據(jù),立即使其可見(jiàn);最終一致性是弱一致性的強(qiáng)化版,系統(tǒng)保證在沒(méi)有后續(xù)更新的前提下,系統(tǒng)最終返回上一次更新操作的值。在沒(méi)有故障發(fā)生的前提下,不一致窗口的時(shí)間主要受通信延遲,系統(tǒng)負(fù)載和復(fù)制副本的個(gè)數(shù)影響。

          CAP 定理

          CAP 定理是由加州大學(xué)伯克利分校 Eric Brewer 教授提出來(lái)的,指的是在一個(gè)分布式系統(tǒng)中, Consistency(一致性)、 Availability(可用性)、Partition tolerance(分區(qū)容錯(cuò)性),三者不可得兼:

          • 一致性(Consistency):在分布式系統(tǒng)中的所有數(shù)據(jù)備份,在同一時(shí)刻是否同樣的值。(對(duì)某個(gè)指定的客戶(hù)端來(lái)說(shuō),讀操作保證能返回最新的寫(xiě)操作結(jié)果)

          • 可用性(Availability):在集群中一部分節(jié)點(diǎn)故障后,集群整體是否還能響應(yīng)客戶(hù)端的讀寫(xiě)請(qǐng)求。(非故障的節(jié)點(diǎn)在合理的時(shí)間內(nèi)返回合理的響應(yīng))

          • 分區(qū)容錯(cuò)性(Partition tolerance):系統(tǒng)如果不能在時(shí)限內(nèi)達(dá)成數(shù)據(jù)一致性,就意味著發(fā)生了分區(qū)的情況,必須就當(dāng)前操作在 C 和 A 之間做出選擇。(分布式系統(tǒng)在遇到某節(jié)點(diǎn)或網(wǎng)絡(luò)分區(qū)故障的時(shí)候,仍然能夠?qū)ν馓峁M(mǎn)足一致性或可用性的服務(wù))



          CAP 定理模型


          微服務(wù)作為分布式系統(tǒng),同樣受 CAP 原理的制約,在 CAP 理論中, C:Consistency、A:Availability、P:Partition tolerance 三者不可同時(shí)滿(mǎn)足,而服務(wù)化中,更多的是提升 A 以及 P,在這個(gè)過(guò)程中不可避免的會(huì)降低對(duì) C 的要求,因此,BASE 理論隨之而來(lái)。

          BASE 理論

          BASE 理論來(lái)源于 ebay 在 2008 年 ACM 中發(fā)表的論文,BASE 理論的基本原則有三個(gè):Basically Available(基本可用),Soft state(軟狀態(tài)),Eventually consistent(最終一致性),主要目的是為了提升分布式系統(tǒng)的可伸縮性,論文同樣闡述了如何對(duì)業(yè)務(wù)進(jìn)行調(diào)整以及折中的手段,BASE 理論是對(duì) CAP 定理中的一致性和可用性進(jìn)行一個(gè)權(quán)衡的結(jié)果,理論的核心思想就是:我們無(wú)法做到強(qiáng)一致,但每個(gè)應(yīng)用都可以根據(jù)自身的業(yè)務(wù)特點(diǎn),采用適當(dāng)?shù)姆绞絹?lái)使系統(tǒng)達(dá)到最終一致性(Eventual consistency)。

          • Basically Available(基本可用):整個(gè)系統(tǒng)在某些不可抗力的情況下,仍然能夠保證 “可用性”,即一定時(shí)間內(nèi)仍然能夠返回一個(gè)明確的結(jié)果

          • Soft state(軟狀態(tài)):同一數(shù)據(jù)的不同副本的狀態(tài),可以不需要實(shí)時(shí)一致

          • Eventually Consistent(最終一致性):同一數(shù)據(jù)的不同副本的狀態(tài),可以不需要實(shí)時(shí)一致,但一定要保證經(jīng)過(guò)一定時(shí)間后仍然是一致的

          在最終一致性的實(shí)現(xiàn)過(guò)程中,最基本的操作就是保證事務(wù)參與者的冪等性,所謂的冪等性,就是業(yè)務(wù)方能夠使用相關(guān)的手段,保證單個(gè)事務(wù)多次提交依然能夠保證達(dá)到同樣的目的。

          分布式事務(wù)

          現(xiàn)在,業(yè)內(nèi)比較常用的分布式事務(wù)解決方案,包括強(qiáng)一致性的兩階段提交模式、三階段提交模式,以及最終一致性的事務(wù)消息模式、補(bǔ)償事務(wù)模式、本地消息表模式、SAGA 模式,我們會(huì)在后面的章節(jié)中詳細(xì)介紹與實(shí)戰(zhàn)。

          兩階段提交(2PC)- 基于 2PC 實(shí)現(xiàn)的分布式事務(wù)

          兩階段提交協(xié)議(The two-phase commit protocol,2PC)是 XA[1]?用于在全局事務(wù)中協(xié)調(diào)多個(gè)資源的機(jī)制,2PC 是一個(gè)非常經(jīng)典的強(qiáng)一致、中心化的原子提交協(xié)議。這里所說(shuō)的中心化是指協(xié)議中有兩類(lèi)節(jié)點(diǎn):一個(gè)是中心化協(xié)調(diào)者節(jié)點(diǎn)(coordinator)和 N 個(gè)參與者節(jié)點(diǎn)(partcipant)。在分布式系統(tǒng)中,每一個(gè)機(jī)器節(jié)點(diǎn)能夠知道自己在執(zhí)行事務(wù)操作過(guò)程是成功或失敗,卻無(wú)法直接獲取其他分布式節(jié)點(diǎn)的執(zhí)行結(jié)果。因此,為保持事務(wù)處理的 ACID,則引入?yún)f(xié)調(diào)者 (即 XA 協(xié)議中的事務(wù)管理器) 來(lái)統(tǒng)一調(diào)度所有分布式節(jié)點(diǎn)的執(zhí)行邏輯,而被調(diào)度的分布式節(jié)點(diǎn)則稱(chēng)為參與者(即 XA 協(xié)議中的資源管理器)。

          兩階段提交協(xié)議,事務(wù)管理器(協(xié)調(diào)者)分兩個(gè)階段來(lái)協(xié)調(diào)資源管理器(參與者),第一階段準(zhǔn)備資源,也就是預(yù)留事務(wù)所需的資源,如果每個(gè)資源管理器都資源預(yù)留成功,則進(jìn)行第二階段資源提交,否則協(xié)調(diào)資源管理器回滾資源。兩階段提交協(xié)議屬于犧牲了一部分可用性來(lái)?yè)Q取一致性的分布式事務(wù)方案。


          2PC 方案總體流程圖


          第一階段:投票階段

          該階段的主要目的在于打探數(shù)據(jù)庫(kù)集群中的各個(gè)參與者是否能夠正常的執(zhí)行事務(wù),具體步驟如下:

          1. 事務(wù)詢(xún)問(wèn):協(xié)調(diào)者向所有的參與者發(fā)送事務(wù)執(zhí)行請(qǐng)求,并等待參與者反饋事務(wù)執(zhí)行結(jié)果。

          2. 事執(zhí)行事務(wù):務(wù)參與者收到請(qǐng)求之后,執(zhí)行事務(wù)但不提交,并將 Undo 和 Redo 信息記入事務(wù)日志中。

          3. 各參與者向協(xié)調(diào)者反饋事務(wù)詢(xún)問(wèn)的響應(yīng):參與者將自己事務(wù)執(zhí)行情況反饋給協(xié)調(diào)者,同時(shí)阻塞等待協(xié)調(diào)者的后續(xù)指令。

          第二階段:事務(wù)提交階段

          在經(jīng)過(guò)第一階段協(xié)調(diào)者的詢(xún)盤(pán)之后,各個(gè)參與者會(huì)回復(fù)自己事務(wù)的執(zhí)行情況,這時(shí)候存在三種可能性:(1)所有的參與者都回復(fù)能夠正常執(zhí)行事務(wù);(2)一個(gè)或多個(gè)參與者回復(fù)事務(wù)執(zhí)行失敗;(3)協(xié)調(diào)者等待超時(shí)

          對(duì)于第一種情況,協(xié)調(diào)者將向所有的參與者發(fā)出提交事務(wù)的通知,具體步驟如下:

          1. 發(fā)送提交請(qǐng)求:協(xié)調(diào)者向各個(gè)參與者發(fā)送 commit 通知,請(qǐng)求提交事務(wù)。

          2. 參事務(wù)提交:參與者收到事務(wù)提交通知之后,執(zhí)行 commit 操作,然后釋放占有的資源。

          3. 反饋事務(wù)提交結(jié)果:參與者向協(xié)調(diào)者返回事務(wù) commit 結(jié)果信息,即向協(xié)調(diào)者發(fā)送 Ack 消息。


          2PC 方案事務(wù)提交流程流程圖


          對(duì)于第二、三種情況,協(xié)調(diào)者均認(rèn)為參與者無(wú)法成功執(zhí)行事務(wù),為了整個(gè)集群數(shù)據(jù)的一致性,所以要向各個(gè)參與者發(fā)送事務(wù)回滾通知,具體步驟如下:

          1. 發(fā)送回滾請(qǐng)求:協(xié)調(diào)者向各個(gè)參與者發(fā)送事務(wù) rollback 通知,請(qǐng)求回滾事務(wù)。

          2. 事務(wù)回滾:參與者收到事務(wù)回滾通知之后,執(zhí)行 rollback 操作,然后釋放占有的資源。

          3. 反饋事務(wù)回滾結(jié)果:參與者向協(xié)調(diào)者返回事務(wù) rollback 結(jié)果信息,即向協(xié)調(diào)者發(fā)送 Ack 消息。



          2PC 方案事務(wù)回滾流程流程圖

          總結(jié)

          兩階段提交協(xié)議原理簡(jiǎn)單、易于實(shí)現(xiàn),但是缺點(diǎn)也是顯而易見(jiàn)的,主要缺點(diǎn)如下:

          • 單點(diǎn)問(wèn)題:協(xié)調(diào)者在整個(gè)兩階段提交過(guò)程中扮演著舉足輕重的作用,一旦協(xié)調(diào)者所在服務(wù)器宕機(jī),就會(huì)影響整個(gè)數(shù)據(jù)庫(kù)集群的正常運(yùn)行,比如在第二階段中,如果協(xié)調(diào)者因?yàn)楣收喜荒苷0l(fā)送事務(wù)提交或回滾通知,那么參與者們將一直處于阻塞狀態(tài),整個(gè)數(shù)據(jù)庫(kù)集群將無(wú)法提供服務(wù)。

          • 同步阻塞:兩階段提交執(zhí)行過(guò)程中,所有的參與者都需要聽(tīng)從協(xié)調(diào)者的統(tǒng)一調(diào)度,期間處于阻塞狀態(tài)而不能從事其他操作,這樣效率極其低下。

          • 數(shù)據(jù)不一致性:兩階段提交協(xié)議雖然是分布式數(shù)據(jù)強(qiáng)一致性所設(shè)計(jì),但仍然存在數(shù)據(jù)不一致性的可能性,比如在第二階段中,假設(shè)協(xié)調(diào)者發(fā)出了事務(wù) commit 通知,但是因?yàn)榫W(wǎng)絡(luò)問(wèn)題該通知僅被一部分參與者所收到并執(zhí)行了 commit 操作,其余的參與者則因?yàn)闆](méi)有收到通知一直處于阻塞狀態(tài),這時(shí)候就產(chǎn)生了數(shù)據(jù)的不一致性。

          何時(shí)選擇兩階段提交分布式事務(wù)?

          兩階段提交分布式事務(wù),在prepare階段需要等待所有參與子事務(wù)的反饋,因此可能造成數(shù)據(jù)庫(kù)資源鎖定時(shí)間過(guò)長(zhǎng),對(duì)性能影響很大,不適合并發(fā)高以及子事務(wù)生命周長(zhǎng)較長(zhǎng)的業(yè)務(wù)場(chǎng)景;因此適用于參與者較少,單個(gè)本地事務(wù)執(zhí)行時(shí)間較少,并且參與者自身可用性很高的場(chǎng)景,否則,其很可能導(dǎo)致性能下降嚴(yán)重。兩階段提交分布式事務(wù)方案屬于犧牲了一部分可用性來(lái)?yè)Q取的一致性。

          三階段提交(3PC)- 基于 3PC 實(shí)現(xiàn)的分布式事務(wù)

          三階段提交協(xié)議(The three-phase commit protocol,3PC)針對(duì)兩階段提交協(xié)議存在的問(wèn)題,將兩階段提交協(xié)議的 “投票階段” 過(guò)程一分為二,在兩階段提交協(xié)議的基礎(chǔ)上增加了 “預(yù)詢(xún)盤(pán)” 階段,以及超時(shí)策略使得原先在兩階段提交協(xié)議中,參與者在投票之后,由于協(xié)調(diào)者發(fā)生崩潰或錯(cuò)誤,而導(dǎo)致參與者處于無(wú)法知曉是否提交或者中止的 “不確定狀態(tài)” 所產(chǎn)生的可能相當(dāng)長(zhǎng)的延時(shí)的問(wèn)題得以解決,從而來(lái)減少整個(gè)集群的阻塞時(shí)間,提升系統(tǒng)性能。三階段提交協(xié)議的三個(gè)階段分別為:can_commit,pre_commit,do_commit。

          第一階段:can_commit 階段

          該階段協(xié)調(diào)者會(huì)去詢(xún)問(wèn)各個(gè)參與者是否能夠正常執(zhí)行事務(wù),參與者根據(jù)自身情況回復(fù)一個(gè)預(yù)估值,相對(duì)于真正的執(zhí)行事務(wù),這個(gè)過(guò)程是輕量的,具體步驟如下:

          1. 事務(wù)詢(xún)問(wèn):協(xié)調(diào)者向各個(gè)參與者發(fā)送事務(wù)詢(xún)問(wèn)通知,詢(xún)問(wèn)是否可以執(zhí)行事務(wù)操作,并等待回復(fù)。

          2. 各參與者向協(xié)調(diào)者反饋事務(wù)詢(xún)問(wèn)的響應(yīng):各個(gè)參與者依據(jù)自身狀況回復(fù)一個(gè)預(yù)估值,如果預(yù)估自己能夠正常執(zhí)行事務(wù)就返回確定信息,并進(jìn)入預(yù)備狀態(tài),否則返回否定信息。

          第二階段:pre_commit 階段

          本階段協(xié)調(diào)者會(huì)根據(jù)第一階段的詢(xún)盤(pán)結(jié)果采取相應(yīng)操作,詢(xún)盤(pán)結(jié)果主要有三種:(1)所有的參與者都返回確定信息;(2)一個(gè)或多個(gè)參與者返回否定信息;(3)協(xié)調(diào)者等待超時(shí)

          針對(duì)第一種情況,協(xié)調(diào)者會(huì)向所有參與者發(fā)送事務(wù)執(zhí)行請(qǐng)求,具體步驟如下:

          1. 發(fā)送預(yù)提交請(qǐng)求:協(xié)調(diào)者向所有的事務(wù)參與者發(fā)送事務(wù)執(zhí)行通知。

          2. 事務(wù)預(yù)提交:參與者收到通知后,執(zhí)行事務(wù)但不提交,并將 Undo 和 Redo 信息記錄到事務(wù)日志中。

          3. 各參與者向協(xié)調(diào)者反饋事務(wù)執(zhí)行的響應(yīng):參與者將自己事務(wù)執(zhí)行情況反饋給協(xié)調(diào)者,同時(shí)阻塞等待協(xié)調(diào)者的后續(xù)指令,提交(commit)或中止(abort)。

          針對(duì)第二、三種情況,協(xié)調(diào)者認(rèn)為事務(wù)無(wú)法正常執(zhí)行,于是向各個(gè)參與者發(fā)出 abort 通知,請(qǐng)求退出預(yù)備狀態(tài),具體步驟如下:

          1. 發(fā)送中斷請(qǐng)求:協(xié)調(diào)者向所有事務(wù)參與者發(fā)送 abort 通知。

          2. 中斷事務(wù):無(wú)論是收到來(lái)自協(xié)調(diào)者的 abort 請(qǐng)求,或者是在等待協(xié)調(diào)者請(qǐng)求過(guò)程中出現(xiàn)超時(shí),參與者都會(huì)中斷事務(wù)。


          3PC 方案 pre_commit 階段失敗流程流程圖


          第三階段:do_commit 階段

          如果第二階段事務(wù)未中斷,那么本階段協(xié)調(diào)者將會(huì)依據(jù)事務(wù)執(zhí)行返回的結(jié)果來(lái)決定提交或回滾事務(wù),分為三種情況:(1)所有的參與者都回復(fù)能夠正常執(zhí)行事務(wù);(2)一個(gè)或多個(gè)參與者回復(fù)事務(wù)執(zhí)行失敗;(3)協(xié)調(diào)者等待超時(shí)

          對(duì)于第一種情況,協(xié)調(diào)者將向所有的參與者發(fā)出提交事務(wù)的通知,具體步驟如下:

          1. 發(fā)送提交請(qǐng)求:協(xié)調(diào)者向各個(gè)參與者發(fā)送 commit 通知,請(qǐng)求提交事務(wù)。

          2. 參事務(wù)提交:參與者收到事務(wù)提交通知之后,執(zhí)行 commit 操作,然后釋放占有的資源。

          3. 反饋事務(wù)提交結(jié)果:參與者向協(xié)調(diào)者返回事務(wù) commit 結(jié)果信息,即向協(xié)調(diào)者發(fā)送 Ack 消息。


          3PC 方案事務(wù)提交流程流程圖


          對(duì)于第二、三種情況,協(xié)調(diào)者均認(rèn)為參與者無(wú)法成功執(zhí)行事務(wù),為了整個(gè)集群數(shù)據(jù)的一致性,所以要向各個(gè)參與者發(fā)送事務(wù)回滾通知,具體步驟如下:

          1. 發(fā)送回滾請(qǐng)求:協(xié)調(diào)者向各個(gè)參與者發(fā)送事務(wù) rollback 通知,請(qǐng)求回滾事務(wù)。

          2. 事務(wù)回滾:參與者收到事務(wù)回滾通知之后,執(zhí)行 rollback 操作,然后釋放占有的資源。

          3. 反饋事務(wù)回滾結(jié)果:參與者向協(xié)調(diào)者返回事務(wù) rollback 結(jié)果信息,即向協(xié)調(diào)者發(fā)送 Ack 消息。


          3PC 方案事務(wù)回滾流程流程圖


          在本階段如果因?yàn)閰f(xié)調(diào)者或網(wǎng)絡(luò)問(wèn)題,導(dǎo)致參與者遲遲不能收到來(lái)自協(xié)調(diào)者的 commit 或 rollback 請(qǐng)求,那么參與者將不會(huì)如兩階段提交協(xié)議中那樣陷入阻塞,而是等待超時(shí)后繼續(xù) commit,相對(duì)于兩階段提交雖然降低了同步阻塞,但仍然無(wú)法完全避免數(shù)據(jù)的不一致。

          總結(jié)

          相比較 2PC 而言,3PC 對(duì)于協(xié)調(diào)者(Coordinator)和參與者(Partcipant)都設(shè)置了超時(shí)時(shí)間,而 2PC 只有協(xié)調(diào)者才擁有超時(shí)機(jī)制。這一優(yōu)化主要避免了參與者在長(zhǎng)時(shí)間無(wú)法與協(xié)調(diào)者節(jié)點(diǎn)通訊(協(xié)調(diào)者掛掉了)的情況下,無(wú)法釋放資源的問(wèn)題,因?yàn)閰⑴c者自身?yè)碛谐瑫r(shí)機(jī)制會(huì)在超時(shí)后,自動(dòng)進(jìn)行本地 commit 從而進(jìn)行釋放資源,而這種機(jī)制也側(cè)面降低了整個(gè)事務(wù)的阻塞時(shí)間和范圍。3PC 在去除阻塞的同時(shí)也引入了新問(wèn)題,當(dāng)參與者接收到 preCommit 消息后,如果網(wǎng)絡(luò)出現(xiàn)分區(qū),此時(shí)協(xié)調(diào)者所在節(jié)點(diǎn)和參與者無(wú)法進(jìn)行正常的網(wǎng)絡(luò)通信,在這種情況下,該參與者依然會(huì)進(jìn)行事務(wù)的提交,這必然出現(xiàn)數(shù)據(jù)的不一致性。

          何時(shí)選擇三階段提交分布式事務(wù)?

          兩階段提交協(xié)議中所存在的長(zhǎng)時(shí)間阻塞狀態(tài)發(fā)生的幾率還是非常低的,所以雖然三階段提交協(xié)議相對(duì)于兩階段提交協(xié)議對(duì)于數(shù)據(jù)強(qiáng)一致性更有保障,但是因?yàn)樾蕟?wèn)題,兩階段提交協(xié)議在實(shí)際系統(tǒng)中反而更加受寵。

          補(bǔ)償事務(wù)(TCC)- 基于 TCC 實(shí)現(xiàn)的分布式事務(wù)

          TCC(Try-Confirm-Cancel)實(shí)際上是服務(wù)化的兩階段提交協(xié)議,是一種達(dá)到最終一致性的補(bǔ)償性事務(wù),相對(duì)于 XA 等傳統(tǒng)模型,其特征在于它不依賴(lài) RM 對(duì)分布式事務(wù)的支持,而是通過(guò)對(duì)業(yè)務(wù)邏輯的分解來(lái)實(shí)現(xiàn)分布式事務(wù)。其核心思想是:”針對(duì)每個(gè)操作都要注冊(cè)一個(gè)與其對(duì)應(yīng)的確認(rèn)和補(bǔ)償(撤銷(xiāo))操作”。它分為三個(gè)階段:Try、Confirm、Cancel,業(yè)務(wù)開(kāi)發(fā)者需要實(shí)現(xiàn)這三個(gè)服務(wù)接口:

          • Try 階段:完成所有業(yè)務(wù)檢查,預(yù)留必須的業(yè)務(wù)資源,所有參與者的 Try 接口都成功了,事務(wù)管理器會(huì)提交事務(wù),并調(diào)用每個(gè)參與者的 Confirm 接口真正提交業(yè)務(wù)操作,否則調(diào)用每個(gè)參與者的 Cancel 接口回滾事務(wù)。

          • Confirm 階段:真正執(zhí)行的業(yè)務(wù)邏輯,不做任何業(yè)務(wù)檢查,只使用 Try 階段預(yù)留的業(yè)務(wù)資源。因此,只要 Try 操作成功,Confirm 必須能成功。另外,Confirm 操作需滿(mǎn)足冪等性,保證一筆分布式事務(wù)能且只能成功一次。

          • Cancel 階段:釋放 Try 階段預(yù)留的業(yè)務(wù)資源。同樣的,Cancel 操作也需要滿(mǎn)足冪等性。

          事務(wù)開(kāi)始時(shí),業(yè)務(wù)應(yīng)用會(huì)向事務(wù)協(xié)調(diào)器注冊(cè)啟動(dòng)事務(wù)。之后業(yè)務(wù)應(yīng)用會(huì)調(diào)用所有服務(wù)的 try 接口,完成一階段準(zhǔn)備。之后事務(wù)協(xié)調(diào)器會(huì)根據(jù) try 接口返回情況,決定調(diào)用 confirm 接口或者 cancel 接口。如果接口調(diào)用失敗,會(huì)進(jìn)行重試。事務(wù)協(xié)調(diào)器記錄了全局事務(wù)的推進(jìn)狀態(tài)以及各子事務(wù)的執(zhí)行狀態(tài),負(fù)責(zé)推進(jìn)各個(gè)子事務(wù)共同進(jìn)行提交或者回滾。同時(shí)負(fù)責(zé)在子事務(wù)處理超時(shí)后不停重試,重試不成功后轉(zhuǎn)手工處理,用以保證事務(wù)的最終一致性。


          TCC 方案整體流程圖

          業(yè)務(wù)場(chǎng)景介紹

          假設(shè)現(xiàn)在有一個(gè)電商系統(tǒng),里面有一個(gè)支付訂單的場(chǎng)景。那對(duì)一個(gè)訂單支付之后,我們需要做下面的步驟:

          [1] 更改訂單的狀態(tài)為“已支付” - 對(duì)本地的的訂單數(shù)據(jù)庫(kù)修改訂單狀態(tài)為 “已支付”
          [2] 扣減商品庫(kù)存 - 調(diào)用庫(kù)存服務(wù)扣減庫(kù)存
          [3] 給會(huì)員增加積分 - 調(diào)用積分服務(wù)增加積分
          [4] 創(chuàng)建銷(xiāo)售出庫(kù)單通知倉(cāng)庫(kù)發(fā)貨 - 調(diào)用倉(cāng)儲(chǔ)服務(wù)通知發(fā)貨

          對(duì)于分布式事務(wù)來(lái)說(shuō),上面那幾個(gè)步驟,要么全部成功,如果任何一個(gè)服務(wù)的操作失敗了,就全部一起回滾,撤銷(xiāo)已經(jīng)完成的操作。

          TCC 實(shí)現(xiàn)階段一:Try

          訂單服務(wù):修改訂單的狀態(tài)為支付中 OrderStatus.UPDATING
          庫(kù)存服務(wù):庫(kù)存數(shù)量不變,可銷(xiāo)售庫(kù)存數(shù)量減 1,設(shè)計(jì)一個(gè)單獨(dú)的凍結(jié)庫(kù)存的字段 freeze_inventory 數(shù)量加 1,表示有 1 個(gè)庫(kù)存被凍結(jié)
          積分服務(wù):會(huì)員積分不變,設(shè)計(jì)一個(gè)單獨(dú)的預(yù)增加積分字段 prepare_add_credit 數(shù)量設(shè)置為 10,表示有 10 個(gè)積分準(zhǔn)備增加
          倉(cāng)儲(chǔ)服務(wù):先創(chuàng)建一個(gè)銷(xiāo)售出庫(kù)單,但是這個(gè)銷(xiāo)售出庫(kù)單的狀態(tài)是 “UNKNOWN”未知

          TCC 實(shí)現(xiàn)階段二:Confirm

          訂單服務(wù):修改訂單的狀態(tài)為已支付 OrderStatus.PAYED
          庫(kù)存服務(wù):將凍結(jié)庫(kù)存的字段 freeze_inventory 數(shù)量清空,表示正式完成了庫(kù)存的扣減
          積分服務(wù):將預(yù)增加積分字段 prepare_add_credit 10 個(gè)積分扣掉,然后加入實(shí)際的會(huì)員積分字段中
          倉(cāng)儲(chǔ)服務(wù):將銷(xiāo)售出庫(kù)單的狀態(tài)正式修改為 “CREATED” 已創(chuàng)建,可以供倉(cāng)儲(chǔ)管理人員查看和使用

          TCC 實(shí)現(xiàn)階段三:Cancel

          訂單服務(wù):修改訂單的狀態(tài)為已取消 OrderStatus.CANCELED
          庫(kù)存服務(wù):將凍結(jié)庫(kù)存的字段 freeze_inventory 1 個(gè)庫(kù)粗扣掉,然后加入可銷(xiāo)售庫(kù)存字段中
          積分服務(wù):將預(yù)增加積分字段 prepare_add_credit 10 個(gè)積分扣掉
          倉(cāng)儲(chǔ)服務(wù):將銷(xiāo)售出庫(kù)單的狀態(tài)正式修改為 “CANCELED” 已取消

          如果使用基于 TCC 實(shí)現(xiàn)的分布式事務(wù),最好選擇某種 TCC 分布式事務(wù)框架, 事務(wù)的 Try、Confirm、Cancel 三個(gè)狀態(tài)交給框架來(lái)感知 。服務(wù)調(diào)用鏈路依次執(zhí)行 Try 邏輯,如果都正常的話(huà),TCC 分布式事務(wù)框架推進(jìn)執(zhí)行 Confirm 邏輯,完成整個(gè)事務(wù);如果某個(gè)服務(wù)的 Try 邏輯有問(wèn)題,TCC 分布式事務(wù)框架感知到之后就會(huì)推進(jìn)執(zhí)行各個(gè)服務(wù)的 Cancel 邏輯,撤銷(xiāo)之前執(zhí)行的各種操作。這里筆者給大家推薦幾個(gè)比較不錯(cuò)的 TCC 框架:ByteTCC,TCC-transaction,Himly。

          TCC 異常控制

          在微服務(wù)架構(gòu)下,很有可能出現(xiàn)網(wǎng)絡(luò)超時(shí)、重發(fā),機(jī)器宕機(jī)等一系列的異常 Case。一旦遇到這些 Case,就會(huì)導(dǎo)致我們的分布式事務(wù)執(zhí)行過(guò)程出現(xiàn)異常。最常見(jiàn)的主要是這三種異常,分別是空回滾、冪等、懸掛。

          允許空回滾

          什么是空回滾?事務(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í)。

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


          TCC 空回滾流程


          防懸掛控制

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

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

          TCC 懸掛流程


          冪等控制

          無(wú)論是網(wǎng)絡(luò)數(shù)據(jù)包重傳,還是異常事務(wù)的補(bǔ)償執(zhí)行,都會(huì)導(dǎo)致 TCC 服務(wù)的 Try、Confirm 或者 Cancel 操作被重復(fù)執(zhí)行;用戶(hù)在實(shí)現(xiàn) TCC 服務(wù)時(shí),需要考慮冪等控制,即 Try、Confirm、Cancel 執(zhí)行一次和執(zhí)行多次的業(yè)務(wù)結(jié)果是一樣的。

          總結(jié)

          TCC 方案的處理流程與 2PC 方案的處理流程類(lèi)似,不過(guò) 2PC 通常都是在跨庫(kù)的 DB 層面,而 TCC 本質(zhì)上就是一個(gè)應(yīng)用層面的 2PC,需要通過(guò)業(yè)務(wù)邏輯來(lái)實(shí)現(xiàn)。這種分布式事務(wù)的實(shí)現(xiàn)方式的優(yōu)勢(shì)在于,可以讓?xiě)?yīng)用自己定義數(shù)據(jù)庫(kù)操作的粒度,使得降低鎖沖突、提高吞吐量成為可能。當(dāng)然 TCC 方案也有不足之處,集中表現(xiàn)在以下兩個(gè)方面:

          • 對(duì)應(yīng)用的侵入性強(qiáng):業(yè)務(wù)邏輯的每個(gè)分支都需要實(shí)現(xiàn) try、confirm、cancel 三個(gè)操作,應(yīng)用侵入性較強(qiáng),改造成本高。

          • 實(shí)現(xiàn)難度較大:需要按照網(wǎng)絡(luò)狀態(tài)、系統(tǒng)故障等不同的失敗原因?qū)崿F(xiàn)不同的回滾策略。為了滿(mǎn)足一致性的要求,confirm 和 cancel 接口必須實(shí)現(xiàn)冪等。

          何時(shí)選擇基于 TCC 實(shí)現(xiàn)的分布式事務(wù)?

          TCC 方案適用于時(shí)效性要求高,如轉(zhuǎn)賬、支付等場(chǎng)景,因此 TCC 方案在電商、金融領(lǐng)域落地較多,但是上述原因?qū)е?TCC 方案大多被研發(fā)實(shí)力較強(qiáng)、有迫切需求的大公司所采用。微服務(wù)倡導(dǎo)服務(wù)的輕量化、易部署,而 TCC 方案中很多事務(wù)的處理邏輯需要應(yīng)用自己編碼實(shí)現(xiàn),對(duì)業(yè)務(wù)的侵入強(qiáng),復(fù)雜且開(kāi)發(fā)量大。因此,TCC 實(shí)際上是最為復(fù)雜的一種情況,其能處理所有的業(yè)務(wù)場(chǎng)景,但無(wú)論出于性能上的考慮,還是開(kāi)發(fā)復(fù)雜度上的考慮,都應(yīng)該盡量避免該類(lèi)事務(wù)。

          SAGA - 基于 Saga 實(shí)現(xiàn)的分布式事務(wù)

          Saga 事務(wù)模型又叫做長(zhǎng)時(shí)間運(yùn)行的事務(wù)(Long-running-transaction), 它是由普林斯頓大學(xué)的 Hector Garcia-Molina 和 Kenneth Salem 等人提出,它描述的是另外一種在沒(méi)有兩階段提交的的情況下解決分布式系統(tǒng)中復(fù)雜的業(yè)務(wù)事務(wù)問(wèn)題。該模型其核心思想就是拆分分布式系統(tǒng)中的長(zhǎng)事務(wù)為多個(gè)短事務(wù),或者叫多個(gè)本地事務(wù),然后由 Sagas 工作流引擎負(fù)責(zé)協(xié)調(diào),如果整個(gè)流程正常結(jié)束,那么就算是業(yè)務(wù)成功完成,如果在這過(guò)程中實(shí)現(xiàn)失敗,那么 Sagas 工作流引擎就會(huì)以相反的順序調(diào)用補(bǔ)償操作,重新進(jìn)行業(yè)務(wù)回滾。

          Saga 的具體實(shí)現(xiàn)分為兩種:協(xié)同式(Choreography) 以及 編排式(Orchestration)

          協(xié)同式(Choreography)

          這種模式下不存在協(xié)調(diào)器的概念,每個(gè)節(jié)點(diǎn)均對(duì)自己的上下游負(fù)責(zé),在監(jiān)聽(tīng)處理上游節(jié)點(diǎn)事件的同時(shí),對(duì)下游節(jié)點(diǎn)發(fā)布事件。把 Saga 的決策和執(zhí)行順序邏輯分布在 Saga 的每一個(gè)參與方中,它們通過(guò)交換事件的方式來(lái)進(jìn)行溝通。

          Saga 協(xié)同式模式流程圖


          編排式(Orchestration)

          把 Saga 的決策和執(zhí)行順序邏輯集中在一個(gè) Saga 編排器類(lèi)中。Saga 編排器發(fā)出命令式消息給每個(gè) Saga 參與方,指示這些參與方服務(wù)完成具體操作。該中心節(jié)點(diǎn),即協(xié)調(diào)器知道整個(gè)事務(wù)的分布狀態(tài),相比于無(wú)中心節(jié)點(diǎn)方式,該方式有著許多優(yōu)點(diǎn):(1)能夠避免事務(wù)之間的循環(huán)依賴(lài)關(guān)系;(2)參與者只需要執(zhí)行命令 / 回復(fù),降低參與者的復(fù)雜性;(3)開(kāi)發(fā)測(cè)試門(mén)檻低;(4)在添加新步驟時(shí),事務(wù)復(fù)雜性保持線(xiàn)性,回滾更容易管理。因此大多數(shù) Saga 模型實(shí)現(xiàn)均采用了這種思路。


          Saga 編排式模式流程圖


          總結(jié)

          Saga 方案的優(yōu)點(diǎn)在于其降低了事務(wù)粒度,使得事務(wù)擴(kuò)展更加容易,同時(shí)采用了異步化方式提升性能。但是其缺點(diǎn)在于很多時(shí)候很難定義補(bǔ)償接口,回滾代價(jià)高,而且由于 Saga 在執(zhí)行過(guò)程中采用了先提交后補(bǔ)償?shù)乃悸愤M(jìn)行操作,所以單個(gè)子事務(wù)在并發(fā)提交時(shí)的隔離性很難保證。

          何時(shí)選擇基于 Saga 實(shí)現(xiàn)的分布式事務(wù)?

          Saga 方案適用于無(wú)需馬上返回業(yè)務(wù)發(fā)起方最終狀態(tài)的場(chǎng)景,例如:你的請(qǐng)求已提交,請(qǐng)稍后查詢(xún)或留意通知之類(lèi)的場(chǎng)景。Saga 方案中所有的本地子事務(wù)執(zhí)行過(guò)程中,都無(wú)需等待其調(diào)用的子事務(wù)執(zhí)行,減少了加鎖的時(shí)間,這在事務(wù)流程較多較長(zhǎng)的業(yè)務(wù)中性能優(yōu)勢(shì)更為明顯。同時(shí),其利用隊(duì)列進(jìn)行進(jìn)行通訊,具有削峰填谷的作用。因此該形式適用于不需要同步返回發(fā)起方執(zhí)行最終結(jié)果、可以進(jìn)行補(bǔ)償、對(duì)性能要求較高、不介意額外編碼的業(yè)務(wù)場(chǎng)景。

          事務(wù)消息 - 基于可靠消息最終一致性實(shí)現(xiàn)的分布式事務(wù)

          基于普通消息的最終一致性分布式事務(wù)方案存在的一致性問(wèn)題:(1)以訂單創(chuàng)建為例,訂單系統(tǒng)先創(chuàng)建訂單(本地事務(wù)),再發(fā)送消息給下游處理;如果訂單創(chuàng)建成功,然而消息沒(méi)有發(fā)送出去,那么下游所有系統(tǒng)都無(wú)法感知到這個(gè)事件,會(huì)出現(xiàn)臟數(shù)據(jù);(2)如果先發(fā)送訂單消息,再創(chuàng)建訂單;那么就有可能消息發(fā)送成功,但是在訂單創(chuàng)建的時(shí)候卻失敗了,此時(shí)下游系統(tǒng)卻認(rèn)為這個(gè)訂單已經(jīng)創(chuàng)建,也會(huì)出現(xiàn)臟數(shù)據(jù)。

          此時(shí)可能有同學(xué)會(huì)想,我們可否將消息發(fā)送和業(yè)務(wù)處理放在同一個(gè)本地事務(wù)中來(lái)進(jìn)行處理,如果業(yè)務(wù)消息發(fā)送失敗,那么本地事務(wù)就回滾,這樣是不是就能解決消息發(fā)送的一致性問(wèn)題呢?

          可能的情況一致性
          訂單處理成功,然后突然宕機(jī),事務(wù)未提交,消息沒(méi)有發(fā)送出去一致
          訂單處理成功,由于網(wǎng)絡(luò)原因或者 MQ 宕機(jī),消息沒(méi)有發(fā)送出去,事務(wù)回滾一致
          訂單處理成功,消息發(fā)送成功,但是 MQ 由于其他原因,導(dǎo)致消息存儲(chǔ)失敗,事務(wù)回滾一致
          訂單處理成功,消息存儲(chǔ)成功,但是 MQ 處理超時(shí),從而 ACK 確認(rèn)失敗,導(dǎo)致發(fā)送方本地事務(wù)回滾不一致

          對(duì)于消息發(fā)送的異常情況分析,我們可以看到,使用基于普通消息的最終一致性分布式事務(wù)方案無(wú)論如何,都無(wú)法保證業(yè)務(wù)處理與消息發(fā)送兩邊的一致性,其根本的原因就在于:遠(yuǎn)程調(diào)用,結(jié)果最終可能為成功、失敗、超時(shí);而對(duì)于超時(shí)的情況,處理方最終的結(jié)果可能是成功,也可能是失敗,調(diào)用方是無(wú)法知曉的。為了保證兩邊數(shù)據(jù)的一致性,我們只能從其他地方尋找新的突破口。

          事物消息

          由于傳統(tǒng)的處理方式無(wú)法解決消息生成者本地事務(wù)處理成功與消息發(fā)送成功兩者的一致性問(wèn)題,因此事務(wù)消息就誕生了,事務(wù)消息特性可以看作是兩階段協(xié)議的消息實(shí)現(xiàn)方式,用以確保在以消息中間件解耦的分布式系統(tǒng)中本地事務(wù)的執(zhí)行和消息的發(fā)送,可以以原子的方式進(jìn)行。

          事務(wù)消息作為一種異步確保型事務(wù),本質(zhì)就是為了解決本地事務(wù)執(zhí)行與消息發(fā)送的原子性問(wèn)題。目前,事務(wù)消息在多種分布式消息中間件中均有實(shí)現(xiàn),但是其實(shí)現(xiàn)方式思路卻各有不同。

          傳統(tǒng)事務(wù)消息實(shí)現(xiàn)

          傳統(tǒng)事務(wù)消息實(shí)現(xiàn),一種思路是依賴(lài)于 AMQP 協(xié)議用來(lái)確保消息發(fā)送成功,AMQP 模式下需要在發(fā)送事務(wù)消息時(shí)進(jìn)行兩階段提交,首先進(jìn)行 tx_select 開(kāi)啟事務(wù),然后再進(jìn)行消息發(fā)送,最后進(jìn)行消息的 commit 或者是 rollback。這個(gè)過(guò)程可以保證在消息發(fā)送成功的同時(shí)本地事務(wù)也一定成功執(zhí)行,但事務(wù)粒度不好控制,而且會(huì)導(dǎo)致性能急劇下降,同時(shí)依然無(wú)法解決本地事務(wù)執(zhí)行與消息發(fā)送的原子性問(wèn)題。

          還有另外一種思路,就是通過(guò)保證多條消息的同時(shí)可見(jiàn)性來(lái)保證事務(wù)一致性。但是此類(lèi)消息事務(wù)實(shí)現(xiàn)機(jī)制更多的是用到事務(wù)循環(huán)(consume-transform-produce)場(chǎng)景中,其本質(zhì)還是用來(lái)保證消息自身事務(wù),并沒(méi)有把外部事務(wù)包含進(jìn)來(lái)。

          RocketMQ 事務(wù)消息實(shí)現(xiàn)

          RocketMQ 事務(wù)消息設(shè)計(jì)則主要是為了解決 Producer 端的消息發(fā)送與本地事務(wù)執(zhí)行的原子性問(wèn)題,RocketMQ 的設(shè)計(jì)中 broker 與 producer 端的雙向通信能力,使得 broker 天生可以作為一個(gè)事務(wù)協(xié)調(diào)者存在;而 RocketMQ 本身提供的存儲(chǔ)機(jī)制,則為事務(wù)消息提供了持久化能力;RocketMQ 的高可用機(jī)制以及可靠消息設(shè)計(jì),則為事務(wù)消息在系統(tǒng)在發(fā)生異常時(shí),依然能夠保證事務(wù)的最終一致性達(dá)成。

          RocketMQ 事務(wù)消息的設(shè)計(jì)流程同樣借鑒了兩階段提交理論,整體交互流程如下圖所示:


          RocketMQ 事務(wù)消息實(shí)現(xiàn)流程


          1. 事務(wù)發(fā)起方首先發(fā)送 prepare 消息到 MQ。

          2. 在發(fā)送 prepare 消息成功后執(zhí)行本地事務(wù)。

          3. 根據(jù)本地事務(wù)執(zhí)行結(jié)果返回 commit 或者是 rollback。

          4. 如果消息是 rollback,MQ 將刪除該 prepare 消息不進(jìn)行下發(fā),如果是 commit 消息,MQ 將會(huì)把這個(gè)消息發(fā)送給 consumer 端。

          5. 如果執(zhí)行本地事務(wù)過(guò)程中,執(zhí)行端掛掉,或者超時(shí),MQ 將會(huì)不停的詢(xún)問(wèn)其同組的其它 producer 來(lái)獲取狀態(tài)。

          6. consumer 端的消費(fèi)成功機(jī)制有 MQ 保證。

          在具體實(shí)現(xiàn)上,RocketMQ 通過(guò)使用 Half Topic 以及 Operation Topic 兩個(gè)內(nèi)部隊(duì)列來(lái)存儲(chǔ)事務(wù)消息推進(jìn)狀態(tài)。其中,Half Topic 對(duì)應(yīng)隊(duì)列中存放著 prepare 消息,Operation Topic 對(duì)應(yīng)的隊(duì)列則存放了 prepare message 對(duì)應(yīng)的 commit/rollback 消息,消息體中則是 prepare message 對(duì)應(yīng)的 offset,服務(wù)端定期掃描消息集群中的事物消息,比對(duì)兩個(gè)隊(duì)列的差值來(lái)找到尚未提交的超時(shí)事務(wù),進(jìn)行回查。

          從用戶(hù)側(cè)來(lái)說(shuō),用戶(hù)需要分別實(shí)現(xiàn)本地事務(wù)執(zhí)行以及本地事務(wù)回查方法,因此只需關(guān)注本地事務(wù)的執(zhí)行狀態(tài)即可;而在 service 層,則對(duì)事務(wù)消息的兩階段提交進(jìn)行了抽象,同時(shí)針對(duì)超時(shí)事務(wù)實(shí)現(xiàn)了回查邏輯,通過(guò)不斷掃描當(dāng)前事務(wù)推進(jìn)狀態(tài),來(lái)不斷反向請(qǐng)求 Producer 端獲取超時(shí)事務(wù)的執(zhí)行狀態(tài),在避免事務(wù)掛起的同時(shí),也避免了 Producer 端的單點(diǎn)故障。而在存儲(chǔ)層,RocketMQ 通過(guò) Bridge 封裝了與底層隊(duì)列存儲(chǔ)的相關(guān)操作,用以操作兩個(gè)對(duì)應(yīng)的內(nèi)部隊(duì)列,用戶(hù)也可以依賴(lài)其它存儲(chǔ)介質(zhì)實(shí)現(xiàn)自己的 service,RocketMQ 會(huì)通過(guò) ServiceProvider 加載進(jìn)來(lái)。

          總結(jié)

          總結(jié)一下關(guān)于事物消息的常見(jiàn)問(wèn)題:

          1. 如果 consumer 消費(fèi)失敗,是否需要 producer 做回滾呢?

          答:事務(wù)消息適用于上游事務(wù)對(duì)下游事務(wù)無(wú)依賴(lài)的場(chǎng)景,即 producer 不會(huì)因?yàn)?consumer 消費(fèi)失敗而做回滾,采用事務(wù)消息的應(yīng)用,其所追求的是高可用和最終一致性,消息消費(fèi)失敗的話(huà),MQ 自己會(huì)負(fù)責(zé)重推消息,直到消費(fèi)成功。因此,事務(wù)消息是針對(duì)生產(chǎn)端而言的,而消費(fèi)端,消費(fèi)端的一致性是通過(guò) MQ 的重試機(jī)制來(lái)完成的。

          1. 如果 consumer 端因?yàn)闃I(yè)務(wù)異常而導(dǎo)致回滾,那么豈不是兩邊最終無(wú)法保證一致性?

          答:基于消息的最終一致性方案必須保證消費(fèi)端在業(yè)務(wù)上的操作沒(méi)障礙,它只允許系統(tǒng)異常的失敗,不允許業(yè)務(wù)上的失敗,比如在你業(yè)務(wù)上拋出個(gè) NPE 之類(lèi)的問(wèn)題,導(dǎo)致你消費(fèi)端執(zhí)行事務(wù)失敗,那就很難做到一致了。

          何時(shí)選擇基于可靠消息的最終一致性實(shí)現(xiàn)的分布式事務(wù)?

          事務(wù)消息較好的解決了事務(wù)的最終一致性問(wèn)題,事務(wù)發(fā)起方僅需要關(guān)注本地事務(wù)執(zhí)行以及實(shí)現(xiàn)回查接口給出事務(wù)狀態(tài)判定等實(shí)現(xiàn),而且在上游事務(wù)峰值高時(shí),可以通過(guò)消息隊(duì)列,避免對(duì)下游服務(wù)產(chǎn)生過(guò)大壓力。所以,事務(wù)消息不僅適用于上游事務(wù)對(duì)下游事務(wù)無(wú)依賴(lài)的場(chǎng)景,還可以與一些傳統(tǒng)分布式事務(wù)架構(gòu)相結(jié)合,而 MQ 的服務(wù)端作為天生的具有高可用能力的協(xié)調(diào)者,使基于可靠消息的最終一致性分布式事務(wù)解決方案,用以滿(mǎn)足各種場(chǎng)景下的分布式事務(wù)需求。

          不過(guò)這種方式技術(shù)實(shí)現(xiàn)的難度比較大,目前主流的開(kāi)源 MQ(ActiveMQ、RabbitMQ、Kafka、RocketMQ)中只有 RocketMQ 實(shí)現(xiàn)對(duì)事物消息的支持,其余 MQ 均未實(shí)現(xiàn)對(duì)事務(wù)消息的支持,因此,如果我們希望強(qiáng)依賴(lài)一個(gè) MQ 的事務(wù)消息來(lái)做到消息最終一致性的話(huà),在目前的情況下,技術(shù)選型上只能去選擇 RocketMQ 來(lái)解決。

          本地消息表 - 基于本地消息最終一致性實(shí)現(xiàn)的分布式事務(wù)

          由于并非所有的 MQ 都支持事務(wù)消息,假如我們不選擇 RocketMQ 來(lái)作為系統(tǒng)的 MQ,是否能夠做到消息的最終一致性呢?答案是可以的。

          基于 MQ 事物消息的分布式事務(wù)方案其實(shí)是對(duì)本地消息表的封裝,將本地消息表基于 MQ 內(nèi)部,其他方面的協(xié)議基本與本地消息表一致。因此,我們可以以事物消息的實(shí)現(xiàn)方式去看待基于本地消息表的分布式事務(wù)方案。

          本地消息表這種實(shí)現(xiàn)方式應(yīng)該是業(yè)界使用最多的,該方案也是目前我參與的項(xiàng)目組所使用的分布式事務(wù)方案,其核心思想是將分布式事務(wù)拆分成本地事務(wù)進(jìn)行處理,通過(guò)消息日志的方式來(lái)異步執(zhí)行,這種思路是來(lái)源于 ebay。


          本地消息表方案實(shí)現(xiàn)流程圖


          方案通過(guò)在事務(wù)主動(dòng)發(fā)起方額外新建事務(wù)消息表,事務(wù)發(fā)起方處理業(yè)務(wù)和記錄事務(wù)消息在本地事務(wù)中完成,保證了業(yè)務(wù)與消息同時(shí)成功持久化;通過(guò)定時(shí)任務(wù)輪詢(xún)事務(wù)消息表的數(shù)據(jù)發(fā)送事務(wù)消息,如果消息投遞失敗,依靠重試機(jī)制重試發(fā)送,發(fā)送成功后將消息狀態(tài)更新或者消息清除;事務(wù)被動(dòng)方基于消息中間件消費(fèi)事務(wù)消息表中的事務(wù),如果處理失敗,那么依賴(lài) MQ 本身的重試來(lái)完成重試執(zhí)行,同時(shí)需要注意重試的冪等行設(shè)計(jì);如果是業(yè)務(wù)上面的失敗,可以給事務(wù)主動(dòng)發(fā)起方發(fā)送一個(gè)業(yè)務(wù)補(bǔ)償消息,通知事務(wù)主動(dòng)發(fā)起方進(jìn)行回滾等操作。事務(wù)主動(dòng)發(fā)起和事務(wù)被動(dòng)方定時(shí)掃描本地消息表,把還沒(méi)處理完成的消息或者失敗的消息再發(fā)送一遍。這樣設(shè)計(jì)可以避免”業(yè)務(wù)處理成功 + 事務(wù)消息發(fā)送失敗”,或”業(yè)務(wù)處理失敗 + 事務(wù)消息發(fā)送成功”的棘手情況出現(xiàn),保證 2 個(gè)系統(tǒng)事務(wù)的數(shù)據(jù)一致性。

          何時(shí)選擇基于本地消息最終一致性實(shí)現(xiàn)的分布式事務(wù)?

          基于本地消息最終一致性分布式事務(wù)是一種非常經(jīng)典的分布式事務(wù)實(shí)現(xiàn)方案,基本避免了分布式事務(wù),實(shí)現(xiàn)了“最終一致性”。該方法從應(yīng)用設(shè)計(jì)開(kāi)發(fā)的角度實(shí)現(xiàn)了消息數(shù)據(jù)的可靠性,消息數(shù)據(jù)的可靠性不依賴(lài)于消息中間件,弱化了對(duì) MQ 中間件特性的依賴(lài)。但是該方案與具體的業(yè)務(wù)場(chǎng)景綁定,耦合性強(qiáng),不可公用。消息數(shù)據(jù)與業(yè)務(wù)數(shù)據(jù)同庫(kù),占用業(yè)務(wù)系統(tǒng)資源。業(yè)務(wù)系統(tǒng)在使用關(guān)系型數(shù)據(jù)庫(kù)的情況下,消息服務(wù)性能會(huì)受到關(guān)系型數(shù)據(jù)庫(kù)并發(fā)性能的局限。

          基于消息實(shí)現(xiàn)的事務(wù)適用于分布式事務(wù)的提交或回滾只取決于事務(wù)發(fā)起方的業(yè)務(wù)需求,其他數(shù)據(jù)源的數(shù)據(jù)變更跟隨發(fā)起方進(jìn)行的業(yè)務(wù)場(chǎng)景。

          總結(jié)

          上述幾種的分布式事務(wù)方案中,筆者大致總結(jié)了其設(shè)計(jì)思路、流程、優(yōu)勢(shì)、劣勢(shì)、使用場(chǎng)景等,相信讀者已經(jīng)有了一定的理解。其實(shí)分布式系統(tǒng)的事務(wù)一致性本身是一個(gè)技術(shù)難題,目前沒(méi)有一種很簡(jiǎn)單很完美的方案能夠應(yīng)對(duì)所有場(chǎng)景。筆者認(rèn)為對(duì)于分布式事務(wù)具體還是要使用者根據(jù)不同的業(yè)務(wù)場(chǎng)景去抉擇,結(jié)合自己的業(yè)務(wù)分析,看看自己的業(yè)務(wù)比較適合哪一種,是在乎強(qiáng)一致,還是最終一致即可。上面對(duì)解決方案只是一些簡(jiǎn)單介紹,如果真正的想要落地,其實(shí)每種方案需要思考的地方都非常多,復(fù)雜度都比較大,所以最后再次提醒一定要判斷好是否使用分布式事務(wù)。

          微服務(wù)興起這幾年涌現(xiàn)出不少分布式事務(wù)框架,比如 ByteTCC、TCC-transaction、TCC-transaction 以及最近很火爆的 Seata。目前筆者也在閱讀、研究 Seata 源碼,如果諸位對(duì)分布式事務(wù)感興趣,我想 Seata 框架是一個(gè)值得研究的框架!

          參考博文

          [1].?對(duì)分布式事務(wù)及兩階段提交、三階段提交的理解
          [2]. 分布式事務(wù):兩階段提交與三階段提交
          [3].?里程碑 | Apache RocketMQ 正式開(kāi)源分布式事務(wù)消息
          [4].?分布式事務(wù) Seata Saga 模式首秀以及三種模式詳解 | Meetup#3 回顧
          [5].?分布式事務(wù) Seata TCC 模式深度解析 | SOFAChannel#4 直播整理


          注腳

          [1]. XA:為了統(tǒng)一標(biāo)準(zhǔn)減少行業(yè)內(nèi)不必要的對(duì)接成本,需要制定標(biāo)準(zhǔn)化的處理模型及接口標(biāo)準(zhǔn),國(guó)際開(kāi)放標(biāo)準(zhǔn)組織 Open Group 定義分布式事務(wù)處理模型 DTP(Distributed Transaction Processing Reference Model),DTP 模型定義 TM 和 RM 之間通訊的接口規(guī)范叫 XA。XA 協(xié)議由 Tuxedo 首先提出的,并交給 X/Open 組織,作為資源管理器 RM(Resource Manager)與事務(wù)管理器 TM(Transaction Manager)之間進(jìn)行通信的接口標(biāo)準(zhǔn)。目前,Oracle、Informix、DB2 和 Sybase 等各大數(shù)據(jù)庫(kù)廠(chǎng)家都提供對(duì) XA 的支持。XA 協(xié)議采用兩階段提交方式來(lái)管理分布式事務(wù)。在 XA 規(guī)范中,數(shù)據(jù)庫(kù)充當(dāng) RM 角色,應(yīng)用需要充當(dāng) TM 的角色,即生成全局的 txId,調(diào)用 XAResource 接口,把多個(gè)本地事務(wù)協(xié)調(diào)為全局統(tǒng)一的分布式事務(wù)。

          source:https://morning-pro.github.io/archives/691d905d.html

          喜歡,在看


          瀏覽 24
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日本黄色小电影网站 | 亚洲无码免费高清视频 | 中国夫妻操网站 | 伦理片一区二区 | 国产乱子伦-区二区三区熟睡91 |