1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        還不會分布式事務(wù)?教你7種解決方案,強烈建議收藏

        共 6353字,需瀏覽 13分鐘

         ·

        2021-09-28 13:06

          Java大聯(lián)盟

          致力于最高效的Java學習

        關(guān)注


        原文:segmentfault.com/a/1190000040321750


        B 站搜索:楠哥教你學Java

        獲取更多優(yōu)質(zhì)視頻教程


        隨著業(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 特性。

        • Atomicity(原子性):一個事務(wù)中的所有操作,要么全部完成,要么全部不完成,不會結(jié)束在中間某個環(huán)節(jié)。事務(wù)在執(zhí)行過程中發(fā)生錯誤,會被恢復(fù)到事務(wù)開始前的狀態(tài),就像這個事務(wù)從來沒有執(zhí)行過一樣。

        • Consistency(一致性):在事務(wù)開始之前和事務(wù)結(jié)束以后,數(shù)據(jù)庫的完整性沒有被破壞。完整性包括外鍵約束、應(yīng)用定義的等約束不會被破壞。

        • Isolation(隔離性):數(shù)據(jù)庫允許多個并發(fā)事務(wù)同時對其數(shù)據(jù)進行讀寫和修改的能力,隔離性可以防止多個事務(wù)并發(fā)執(zhí)行時由于交叉執(zhí)行而導(dǎo)致數(shù)據(jù)的不一致。

        • Durability(持久性):事務(wù)處理結(jié)束后,對數(shù)據(jù)的修改就是永久的,即便系統(tǒng)故障也不會丟失。

        分布式事務(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)容非常多,感興趣的同學,可以參考BASE理論):

        基本業(yè)務(wù)可用性(Basic Availability)
        柔性狀態(tài)(Soft state)
        最終一致性(Eventual consistency)
        同樣的,分布式事務(wù)也部分遵循 ACID 規(guī)范:

        原子性:嚴格遵循
        一致性:事務(wù)完成后的一致性嚴格遵循;事務(wù)中的一致性可適當放寬
        隔離性:并行事務(wù)間不可影響;事務(wù)中間結(jié)果可見性允許安全放寬
        持久性:嚴格遵循

        分布式事務(wù)的解決方案

        兩階段提交/XA

        XA是由X/Open組織提出的分布式事務(wù)的規(guī)范,XA規(guī)范主要定義了(全局)事務(wù)管理器(TM)和(局部)資源管理器(RM)之間的接口。本地的數(shù)據(jù)庫如mysql在XA中扮演的是RM角色

        XA一共分為兩階段:

        第一階段(prepare):即所有的參與者RM準備執(zhí)行事務(wù)并鎖住需要的資源。參與者ready時,向TM報告已準備就緒。
        第二階段 (commit/rollback):當事務(wù)管理者(TM)確認所有參與者(RM)都ready后,向所有參與者發(fā)送commit命令。
        目前主流的數(shù)據(jù)庫基本都支持XA事務(wù),包括mysql、oracle、sqlserver、postgre

        XA 事務(wù)由一個或多個資源管理器(RM)、一個事務(wù)管理器(TM)和一個應(yīng)用程序(ApplicationProgram)組成。

        把上面的轉(zhuǎn)賬作為例子,一個成功完成的XA事務(wù)時序圖如下:

        如果有任何一個參與者prepare失敗,那么TM會通知所有完成prepare的參與者進行回滾。

        XA事務(wù)的特點是:

        • 簡單易理解,開發(fā)較容易

        • 對資源進行了長時間的鎖定,并發(fā)度低

        如果讀者想要進一步研究XA,go語言可參考DTM,java語言可參考seata

        SAGA

        Saga是這一篇數(shù)據(jù)庫論文saga提到的一個方案。其核心思想是將長事務(wù)拆分為多個本地短事務(wù),由Saga事務(wù)協(xié)調(diào)器協(xié)調(diào),如果正常結(jié)束那就正常完成,如果某個步驟失敗,則根據(jù)相反順序一次調(diào)用補償操作。

        把上面的轉(zhuǎn)賬作為例子,一個成功完成的SAGA事務(wù)時序圖如下:



        SAGA事務(wù)的特點:

        • 并發(fā)度高,不用像XA事務(wù)那樣長期鎖定資源

        • 需要定義正常操作以及補償操作,開發(fā)量比XA大

        • 一致性較弱,對于轉(zhuǎn)賬,可能發(fā)生A用戶已扣款,最后轉(zhuǎn)賬又失敗的情況

        論文里面的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個階段

        • Try 階段:嘗試執(zhí)行,完成所有業(yè)務(wù)檢查(一致性), 預(yù)留必須業(yè)務(wù)資源(準隔離性)

        • Confirm 階段:確認執(zhí)行真正執(zhí)行業(yè)務(wù),不作任何業(yè)務(wù)檢查,只使用 Try 階段預(yù)留的業(yè)務(wù)資源,Confirm 操作要求具備冪等設(shè)計,Confirm 失敗后需要進行重試。

        • Cancel 階段:取消執(zhí)行,釋放 Try 階段預(yù)留的業(yè)務(wù)資源。Cancel 階段的異常和 Confirm 階段異常處理方案基本上一致,要求滿足冪等設(shè)計。

        把上面的轉(zhuǎn)賬作為例子,通常會在Try里面凍結(jié)金額,但不扣款,Confirm里面扣款,Cancel里面解凍金額,一個成功完成的TCC事務(wù)時序圖如下:


        TCC特點如下:

        • 并發(fā)度較高,無長期資源鎖定。

        • 開發(fā)量較大,需要提供Try/Confirm/Cancel接口。

        • 一致性較好,不會發(fā)生SAGA已扣款最后又轉(zhuǎn)賬失敗的情況

        • TCC適用于訂單類業(yè)務(wù),對中間狀態(tài)有約束的業(yè)務(wù)

        如果讀者想要進一步研究TCC,go語言可參考DTM,java語言可參考seata

        本地消息表

        本地消息表這個方案最初是 ebay 架構(gòu)師 Dan Pritchett 在 2008 年發(fā)表給 ACM 的文章。設(shè)計核心是將需要分布式處理的任務(wù)通過消息的方式來異步確保執(zhí)行。

        大致流程如下:

        寫本地消息和業(yè)務(wù)操作放在一個事務(wù)里,保證了業(yè)務(wù)和發(fā)消息的原子性,要么他們?nèi)汲晒?,要么全都失敗?br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">

        容錯機制:

        • 扣減余額事務(wù) 失敗時,事務(wù)直接回滾,無后續(xù)步驟

        • 輪序生產(chǎn)消息失敗, 增加余額事務(wù)失敗都會進行重試

        本地消息表的特點:

        • 長事務(wù)僅需要分拆成多個任務(wù),使用簡單

        • 生產(chǎn)者需要額外的創(chuàng)建消息表

        • 每個本地消息表都需要進行輪詢

        • 消費者的邏輯如果無法通過重試成功,那么還需要更多的機制,來回滾操作

        適用于可異步執(zhí)行的業(yè)務(wù),且后續(xù)操作無需回滾的業(yè)務(wù)

        事務(wù)消息

        在上述的本地消息表方案中,生產(chǎn)者需要額外創(chuàng)建消息表,還需要對本地消息表進行輪詢,業(yè)務(wù)負擔較重。阿里開源的RocketMQ 4.3之后的版本正式支持事務(wù)消息,該事務(wù)消息本質(zhì)上是把本地消息表放到RocketMQ上,解決生產(chǎn)端的消息發(fā)送與本地事務(wù)執(zhí)行的原子性問題。

        事務(wù)消息發(fā)送及提交:

        • 發(fā)送消息(half消息)

        • 服務(wù)端存儲消息,并響應(yīng)消息的寫入結(jié)果

        • 根據(jù)發(fā)送結(jié)果執(zhí)行本地事務(wù)(如果寫入失敗,此時half消息對業(yè)務(wù)不可見,本地邏輯不執(zhí)行)

        • 根據(jù)本地事務(wù)狀態(tài)執(zhí)行Commit或者Rollback(Commit操作發(fā)布消息,消息對消費者可見)

        正常發(fā)送的流程圖如下:


        補償流程:

        對沒有Commit/Rollback的事務(wù)消息(pending狀態(tài)的消息),從服務(wù)端發(fā)起一次“回查”
        Producer收到回查消息,返回消息對應(yīng)的本地事務(wù)的狀態(tài),為Commit或者Rollback
        事務(wù)消息方案與本地消息表機制非常類似,區(qū)別主要在于原先相關(guān)的本地表操作替換成了一個反查接口

        事務(wù)消息特點如下:

        • 長事務(wù)僅需要分拆成多個任務(wù),并提供一個反查接口,使用簡單

        • 消費者的邏輯如果無法通過重試成功,那么還需要更多的機制,來回滾操作

        適用于可異步執(zhí)行的業(yè)務(wù),且后續(xù)操作無需回滾的業(yè)務(wù)

        如果讀者想要進一步研究事務(wù)消息,可參考rocketmq,為了方便大家學習事務(wù)消息,DTM也提供了簡單實現(xiàn)

        最大努力通知

        發(fā)起通知方通過一定的機制最大努力將業(yè)務(wù)處理結(jié)果通知到接收方。具體包括:

        有一定的消息重復(fù)通知機制。因為接收通知方可能沒有接收到通知,此時要有一定的機制對消息重復(fù)通知。
        消息校對機制。如果盡最大努力也沒有通知到接收方,或者接收方消費消息后要再次消費,此時可由接收方主動向通知方查詢消息信息來滿足需求。
        前面介紹的的本地消息表和事務(wù)消息都屬于可靠消息,與這里介紹的最大努力通知有什么不同?

        可靠消息一致性,發(fā)起通知方需要保證將消息發(fā)出去,并且將消息發(fā)到接收通知方,消息的可靠性關(guān)鍵由發(fā)起通知方來保證。

        最大努力通知,發(fā)起通知方盡最大的努力將業(yè)務(wù)處理結(jié)果通知為接收通知方,但是可能消息接收不到,此時需要接收通知方主動調(diào)用發(fā)起通知方的接口查詢業(yè)務(wù)處理結(jié)果,通知的可靠性關(guān)鍵在接收通知方。

        解決方案上,最大努力通知需要:

        • 提供接口,讓接受通知放能夠通過接口查詢業(yè)務(wù)處理結(jié)果

        • 消息隊列ACK機制,消息隊列按照間隔1min、5min、10min、30min、1h、2h、5h、10h的方式,逐步拉大通知間隔 ,直到達到通知要求的時間窗口上限。之后不再通知

        最大努力通知適用于業(yè)務(wù)通知類型,例如微信交易的結(jié)果,就是通過最大努力通知方式通知各個商戶,既有回調(diào)通知,也有交易查詢接口

        AT事務(wù)模式

        這是阿里開源項目seata中的一種事務(wù)模式,在螞蟻金服也被稱為FMT。優(yōu)點是該事務(wù)模式使用方式,類似XA模式,業(yè)務(wù)無需編寫各類補償操作,回滾由框架自動完成,缺點也類似AT,存在較長時間的鎖,不滿足高并發(fā)的場景。有興趣的同學可以參考seata-AT

        分布式事務(wù)中的網(wǎng)絡(luò)異常

        在分布式事務(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)原因是當一個分支事務(wù)所在服務(wù)宕機或網(wǎng)絡(luò)異常,分支事務(wù)調(diào)用記錄為失敗,這個時候其實是沒有執(zhí)行Try階段,當故障恢復(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 請求才到達參與者真正執(zhí)行。

        下面看一個網(wǎng)絡(luò)異常的時序圖,更好的理解上述幾種問題


        業(yè)務(wù)處理請求4的時候,Cancel在Try之前執(zhí)行,需要處理空回滾
        業(yè)務(wù)處理請求6的時候,Cancel重復(fù)執(zhí)行,需要冪等
        業(yè)務(wù)處理請求8的時候,Try在Cancel后執(zhí)行,需要處理懸掛

        面對上述復(fù)雜的網(wǎng)絡(luò)異常情況,目前看到各家建議的方案都是業(yè)務(wù)方通過唯一鍵,去查詢相關(guān)聯(lián)的操作是否已完成,如果已完成則直接返回成功。相關(guān)的判斷邏輯較復(fù)雜,易出錯,業(yè)務(wù)負擔重。

        在項目DTM中,出現(xiàn)了一種子事務(wù)屏障技術(shù),使用該技術(shù),能夠達到這個效果,看示意圖:


        所有這些請求,到了子事務(wù)屏障后:不正常的請求,會被過濾;正常請求,通過屏障。開發(fā)者使用子事務(wù)屏障之后,前面所說的各種異常全部被妥善處理,業(yè)務(wù)開發(fā)人員只需要關(guān)注實際的業(yè)務(wù)邏輯,負擔大大降低。
        子事務(wù)屏障提供了方法ThroughBarrierCall,方法的原型為:

        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ù)屏障技術(shù)的原理是,在本地數(shù)據(jù)庫,建立分支事務(wù)狀態(tài)表sub_trans_barrier,唯一鍵為全局事務(wù)id-子事務(wù)id-子事務(wù)分支名稱(try|confirm|cancel)

        • 開啟事務(wù)

        • 如果是Try分支,則那么insert ignore插入gid-branchid-try,如果成功插入,則調(diào)用屏障內(nèi)邏輯

        • 如果是Confirm分支,那么insert ignore插入gid-branchid-confirm,如果成功插入,則調(diào)用屏障內(nèi)邏輯

        • 如果是Cancel分支,那么insert ignore插入gid-branchid-try,再插入gid-branchid-cancel,如果try未插入并且cancel插入成功,則調(diào)用屏障內(nèi)邏輯

        • 屏障內(nèi)邏輯返回成功,提交事務(wù),返回成功

        • 屏障內(nèi)邏輯返回錯誤,回滾事務(wù),返回錯誤

        在此機制下,解決了網(wǎng)絡(luò)異常相關(guān)的問題

        • 空補償控制--如果Try沒有執(zhí)行,直接執(zhí)行了Cancel,那么Cancel插入gid-branchid-try會成功,不走屏障內(nèi)的邏輯,保證了空補償控制

        • 冪等控制--任何一個分支都無法重復(fù)插入唯一鍵,保證了不會重復(fù)執(zhí)行

        • 防懸掛控制--Try在Cancel之后執(zhí)行,那么插入的gid-branchid-try不成功,就不執(zhí)行,保證了防懸掛控制

        對于SAGA事務(wù),也是類似的機制。

        子事務(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ù)。


        推薦閱讀

        1、Spring Boot+Vue項目實戰(zhàn)

        2、B站:4小時上手MyBatis Plus

        3、一文搞懂前后端分離

        4、快速上手Spring Boot+Vue前后端分離


        楠哥簡介

        資深 Java 工程師,微信號 southwindss

        《Java零基礎(chǔ)實戰(zhàn)》一書作者

        騰訊課程官方 Java 面試官,今日頭條認證大V

        GitChat認證作者,B站認證UP主(楠哥教你學Java)

        致力于幫助萬千 Java 學習者持續(xù)成長。




        有收獲,就在看 
        瀏覽 47
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            久肏网| 免费A级毛片无码专区 | 日本日逼视频网站 | 一级片黄色免费 | 他揉捏她两乳不停呻吟a视频 | 69视频免费观看 | 国产在线色视频 | 91aaa在线观看 | 69国产精品成人无码视频色 | 小怮女一级A片 |