<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>

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

          共 6353字,需瀏覽 13分鐘

           ·

          2021-09-28 13:06

            Java大聯(lián)盟

            致力于最高效的Java學習

          關注


          原文:segmentfault.com/a/1190000040321750


          B 站搜索:楠哥教你學Java

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


          隨著業(yè)務的快速發(fā)展、業(yè)務復雜度越來越高,幾乎每個公司的系統(tǒng)都會從單體走向分布式,特別是轉(zhuǎn)向微服務架構。隨之而來就必然遇到分布式事務這個難題,這篇文章總結(jié)了分布式事務最經(jīng)典的解決方案,分享給大家。

          基礎理論

          在講解具體方案之前,我們先了解一下分布式事務所涉及到的基礎理論知識。

          我們拿轉(zhuǎn)賬作為例子,A需要轉(zhuǎn)100元給B,那么需要給A的余額-100元,給B的余額+100元,整個轉(zhuǎn)賬要保證,A-100和B+100同時成功,或者同時失敗??纯丛诟鞣N場景下,是如何解決這個問題的。

          事務

          把多條語句作為一個整體進行操作的功能,被稱為數(shù)據(jù)庫事務。數(shù)據(jù)庫事務可以確保該事務范圍內(nèi)的所有操作都可以全部成功或者全部失敗。

          事務具有 4 個屬性:原子性、一致性、隔離性、持久性。這四個屬性通常稱為 ACID 特性。

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

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

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

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

          分布式事務

          銀行跨行轉(zhuǎn)賬業(yè)務是一個典型分布式事務場景,假設A需要跨行轉(zhuǎn)賬給B,那么就涉及兩個銀行的數(shù)據(jù),無法通過一個數(shù)據(jù)庫的本地事務保證轉(zhuǎn)賬的ACID,只能夠通過分布式事務來解決。

          分布式事務就是指事務的發(fā)起者、資源及資源管理器和事務協(xié)調(diào)者分別位于分布式系統(tǒng)的不同節(jié)點之上。在上述轉(zhuǎn)賬的業(yè)務中,用戶A-100操作和用戶B+100操作不是位于同一個節(jié)點上。本質(zhì)上來說,分布式事務就是為了保證在分布式場景下,數(shù)據(jù)操作的正確執(zhí)行。

          分布式事務在分布式環(huán)境下,為了滿足可用性、性能與降級服務的需要,降低一致性與隔離性的要求,一方面遵循 BASE 理論(BASE相關理論,涉及內(nèi)容非常多,感興趣的同學,可以參考BASE理論):

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

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

          分布式事務的解決方案

          兩階段提交/XA

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

          XA一共分為兩階段:

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

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

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

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

          XA事務的特點是:

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

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

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

          SAGA

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

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



          SAGA事務的特點:

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

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

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

          論文里面的SAGA內(nèi)容較多,包括兩種恢復策略,包括分支事務并發(fā)執(zhí)行,我們這里的討論,僅包括最簡單的SAGA

          SAGA適用的場景較多,長事務適用,對中間結(jié)果不敏感的業(yè)務場景適用

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

          TCC

          關于 TCC(Try-Confirm-Cancel)的概念,最早是由 Pat Helland 于 2007 年發(fā)表的一篇名為《Life beyond Distributed Transactions:an Apostate’s Opinion》的論文提出。

          TCC分為3個階段

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

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

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

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


          TCC特點如下:

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

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

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

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

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

          本地消息表

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

          大致流程如下:

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

          容錯機制:

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

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

          本地消息表的特點:

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

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

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

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

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

          事務消息

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

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

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

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

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

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

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


          補償流程:

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

          事務消息特點如下:

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

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

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

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

          最大努力通知

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

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

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

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

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

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

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

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

          AT事務模式

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

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

          在分布式事務的各個環(huán)節(jié)都有可能出現(xiàn)網(wǎng)絡以及業(yè)務故障等問題,這些問題需要分布式事務的業(yè)務方做到防空回滾,冪等,防懸掛三個特性,下面以TCC事務說明這些異常情況:

          空回滾:

            在沒有調(diào)用 TCC 資源 Try 方法的情況下,調(diào)用了二階段的 Cancel 方法,Cancel 方法需要識別出這是一個空回滾,然后直接返回成功。

            出現(xiàn)原因是當一個分支事務所在服務宕機或網(wǎng)絡異常,分支事務調(diào)用記錄為失敗,這個時候其實是沒有執(zhí)行Try階段,當故障恢復后,分布式事務進行回滾則會調(diào)用二階段的Cancel方法,從而形成空回滾。

          冪等

            由于任何一個請求都可能出現(xiàn)網(wǎng)絡異常,出現(xiàn)重復請求,所以所有的分布式事務分支,都需要保證冪等性

          懸掛:

            懸掛就是對于一個分布式事務,其二階段 Cancel 接口比 Try 接口先執(zhí)行。

            出現(xiàn)原因是在 RPC 調(diào)用分支事務try時,先注冊分支事務,再執(zhí)行RPC調(diào)用,如果此時 RPC 調(diào)用的網(wǎng)絡發(fā)生擁堵,RPC 超時以后,TM就會通知RM回滾該分布式事務,可能回滾完成后,RPC 請求才到達參與者真正執(zhí)行。

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


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

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

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


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

          func ThroughBarrierCall(db *sql.DB, transInfo *TransInfo, busiCall BusiFunc)

          業(yè)務開發(fā)人員,在busiCall里面編寫自己的相關邏輯,調(diào)用該函數(shù)。ThroughBarrierCall保證,在空回滾、懸掛等場景下,busiCall不會被調(diào)用;在業(yè)務被重復調(diào)用時,有冪等控制,保證只被提交一次。

          子事務屏障會管理TCC、SAGA、XA、事務消息等,也可以擴展到其他領域

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

          • 開啟事務

          • 如果是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)邏輯返回成功,提交事務,返回成功

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

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

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

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

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

          對于SAGA事務,也是類似的機制。

          子事務屏障技術,為DTM首創(chuàng),它的意義在于設計簡單易實現(xiàn)的算法,提供了簡單易用的接口,在首創(chuàng),它的意義在于設計簡單易實現(xiàn)的算法,提供了簡單易用的接口,在這兩項的幫助下,開發(fā)人員徹底的從網(wǎng)絡異常的處理中解放出來。

          該技術目前需要搭配DTM事務管理器,目前SDK已經(jīng)提供給go語言的開發(fā)者。其他語言的sdk正在規(guī)劃中。對于其他的分布式事務框架,只要提供了合適的分布式事務信息,能夠按照上述原理,快速實現(xiàn)該技術。


          推薦閱讀

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

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

          3、一文搞懂前后端分離

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


          楠哥簡介

          資深 Java 工程師,微信號 southwindss

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

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

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

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




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

          手機掃一掃分享

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

          手機掃一掃分享

          分享
          舉報
          <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>
                  婷婷丁香五月天欧美 | 黄片网站在线免费看 | 黄色视频在线观看地址 | 中文字幕乱码在线 | 中文字幕2区 |