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>

        七種分布式事務的解決方案,一次講給你聽

        共 5705字,需瀏覽 12分鐘

         ·

        2021-03-07 23:16

        本文約5300字,完整閱讀大概會花費你「13分鐘」左右的時間
        [如果你覺得文章對你有幫助,歡迎關注,點贊,轉發(fā)]

        什么是分布式事務

        分布式事務是指事務的參與者、支持事務的服務器、資源服務器以及事務管理器「分別位于不同的分布式系統(tǒng)的不同節(jié)點之上」。

        一個大的操作由N多的小的操作共同完成。而這些小的操作又分布在不同的服務上。針對于這些操作,「要么全部成功執(zhí)行,要么全部不執(zhí)行」。

        為什么會有分布式事務?

        舉個例子:

        轉賬是最經(jīng)典的分布式事務場景,假設用戶 A 使用銀行 app 發(fā)起一筆跨行轉賬給用戶 B,銀行系統(tǒng)首先扣掉用戶 A 的錢,然后增加用戶 B 賬戶中的余額。

        如果其中某個步驟失敗,此時就有可能會出現(xiàn) 2 種「異?!?/strong>情況:

        • 1.用戶 A 的賬戶扣款成功,用戶 B 賬戶余額增加失敗
        • 2.用戶 A 賬戶扣款失敗,用戶 B 賬戶余額增加成功。

        對于銀行系統(tǒng)來說,以上 2 種情況都是「不允許發(fā)生」,此時就需要事務來保證轉賬操作的成功。

        「單體應用」中,我們只需要貼上@Transactional注解就可以開啟事務來保證整個操作的「原子性」。

        但是看似以上簡單的操作,在實際的應用架構中,不可能是單體的服務,我們會把這一系列操作交給「N個服務」去完成,也就是拆分成為「分布式微服務架構」

        比如下訂單服務,扣庫存服務等等,必須要「保證不同服務狀態(tài)結果的一致性」,于是就出現(xiàn)了分布式事務。

        分布式理論

        CAP定理

        在一個分布式系統(tǒng)中,以下三點特性無法同時滿足,「魚與熊掌不可兼得」

        一致性(C):
        在分布式系統(tǒng)中的所有數(shù)據(jù)備份,「在同一時刻是否擁有同樣的值」。(等同于所有節(jié)點訪問同一份最新的數(shù)據(jù)副本)

        可用性(A):
        在集群中一部分節(jié)點「故障」后,集群整體「是否還能響應」客戶端的讀寫請求。(對數(shù)據(jù)更新具備高可用性)

        分區(qū)容錯性(P):
        即使出現(xiàn)「單個組件無法可用,操作依然可以完成」。

        具體地講在分布式系統(tǒng)中,在任何數(shù)據(jù)庫設計中,一個Web應用「至多只能同時支持上面的兩個屬性」。顯然,任何橫向擴展策略都要依賴于數(shù)據(jù)分區(qū)。因此,設計人員必須在一致性與可用性之間做出選擇。

        BASE理論

        在分布式系統(tǒng)中,我們往往追求的是可用性,它的重要程序比一致性要高,那么如何實現(xiàn)高可用性呢?

        前人已經(jīng)給我們提出來了另外一個理論,就是BASE理論,它是用來對CAP定理進行進一步擴充的。BASE理論指的是:

        • 「Basically Available(基本可用)」
        • 「Soft state(軟狀態(tài))」
        • 「Eventually consistent(最終一致性)」

        BASE理論是對CAP中的一致性和可用性進行一個權衡的結果,理論的核心思想就是:我們無法做到強一致,但每個應用都可以根據(jù)自身的業(yè)務特點,采用適當?shù)姆绞絹硎瓜到y(tǒng)達到最終一致性(Eventual consistency)。

        分布式事務解決方案

        兩階段提交(2PC)

        熟悉mysql的同學對兩階段提交應該頗為熟悉,mysql的事務就是通過「日志系統(tǒng)」來完成兩階段提交的。

        兩階段協(xié)議可以用于單機集中式系統(tǒng),由事務管理器協(xié)調多個資源管理器;也可以用于分布式系統(tǒng),「由一個全局的事務管理器協(xié)調各個子系統(tǒng)的局部事務管理器完成兩階段提交」

        這個協(xié)議有「兩個角色」,

        A節(jié)點是事務的協(xié)調者,B和C是事務的參與者。

        事務的提交分成兩個階段

        第一個階段是「投票階段」

        • 1.協(xié)調者首先將命令「寫入日志」
        • 2. 「發(fā)一個prepare命令」給B和C節(jié)點這兩個參與者
        • 3.B和C收到消息后,根據(jù)自己的實際情況,「判斷自己的實際情況是否可以提交」
        • 4.將處理結果「記錄到日志」系統(tǒng)
        • 5.將結果「返回」給協(xié)調者

        第二個階段是「決定階段」

        當A節(jié)點收到B和C參與者所有的確認消息后

        • 「判斷」所有協(xié)調者「是否都可以提交」
          • 如果可以則「寫入日志」并且發(fā)起commit命令
          • 有一個不可以則「寫入日志」并且發(fā)起abort命令
        • 參與者收到協(xié)調者發(fā)起的命令,「執(zhí)行命令」
        • 將執(zhí)行命令及結果「寫入日志」
        • 「返回結果」給協(xié)調者

        可能會存在哪些問題?

        • 「單點故障」:一旦事務管理器出現(xiàn)故障,整個系統(tǒng)不可用

        • 「數(shù)據(jù)不一致」:在階段二,如果事務管理器只發(fā)送了部分 commit 消息,此時網(wǎng)絡發(fā)生異常,那么只有部分參與者接收到 commit 消息,也就是說只有部分參與者提交了事務,使得系統(tǒng)數(shù)據(jù)不一致。

        • 「響應時間較長」:整個消息鏈路是串行的,要等待響應結果,不適合高并發(fā)的場景

        • 「不確定性」:當事務管理器發(fā)送 commit 之后,并且此時只有一個參與者收到了 commit,那么當該參與者與事務管理器同時宕機之后,重新選舉的事務管理器無法確定該條消息是否提交成功。

        三階段提交(3PC)

        三階段提交又稱3PC,相對于2PC來說增加了CanCommit階段和超時機制。如果段時間內沒有收到協(xié)調者的commit請求,那么就會自動進行commit,解決了2PC單點故障的問題。

        但是性能問題和不一致問題仍然沒有根本解決。下面我們還是一起看下三階段流程的是什么樣的?

        • 第一階段:「CanCommit階段」這個階段所做的事很簡單,就是協(xié)調者詢問事務參與者,你是否有能力完成此次事務。

          • 如果都返回yes,則進入第二階段
          • 有一個返回no或等待響應超時,則中斷事務,并向所有參與者發(fā)送abort請求
        • 第二階段:「PreCommit階段」此時協(xié)調者會向所有的參與者發(fā)送PreCommit請求,參與者收到后開始執(zhí)行事務操作,并將Undo和Redo信息記錄到事務日志中。參與者執(zhí)行完事務操作后(此時屬于未提交事務的狀態(tài)),就會向協(xié)調者反饋“Ack”表示我已經(jīng)準備好提交了,并等待協(xié)調者的下一步指令。

        • 第三階段:「DoCommit階段」在階段二中如果所有的參與者節(jié)點都可以進行PreCommit提交,那么協(xié)調者就會從“預提交狀態(tài)”轉變?yōu)椤疤峤粻顟B(tài)”。然后向所有的參與者節(jié)點發(fā)送"doCommit"請求,參與者節(jié)點在收到提交請求后就會各自執(zhí)行事務提交操作,并向協(xié)調者節(jié)點反饋“Ack”消息,協(xié)調者收到所有參與者的Ack消息后完成事務。相反,如果有一個參與者節(jié)點未完成PreCommit的反饋或者反饋超時,那么協(xié)調者都會向所有的參與者節(jié)點發(fā)送abort請求,從而中斷事務。

        補償事務(TCC)

        TCC其實就是采用的補償機制,其核心思想是:「針對每個操作,都要注冊一個與其對應的確認和補償(撤銷)操作」。它分為三個階段:

        「Try,Confirm,Cancel」

        • Try階段主要是對「業(yè)務系統(tǒng)做檢測及資源預留」,其主要分為兩個階段
          • Confirm 階段主要是對「業(yè)務系統(tǒng)做確認提交」,Try階段執(zhí)行成功并開始執(zhí)行 Confirm階段時,默認 Confirm階段是不會出錯的。即:只要Try成功,Confirm一定成功。
          • Cancel 階段主要是在業(yè)務執(zhí)行錯誤,需要回滾的狀態(tài)下執(zhí)行的業(yè)務取消,「預留資源釋放」。

        比如下一個訂單減一個庫存:

        執(zhí)行流程:

        • Try階段:訂單系統(tǒng)將當前訂單狀態(tài)設置為支付中,庫存系統(tǒng)校驗當前剩余庫存數(shù)量是否大于1,然后將可用庫存數(shù)量設置為庫存剩余數(shù)量-1,
          • 如果Try階段「執(zhí)行成功」,執(zhí)行Confirm階段,將訂單狀態(tài)修改為支付成功,庫存剩余數(shù)量修改為可用庫存數(shù)量
          • 如果Try階段「執(zhí)行失敗」,執(zhí)行Cancel階段,將訂單狀態(tài)修改為支付失敗,可用庫存數(shù)量修改為庫存剩余數(shù)量

        TCC 事務機制相比于上面介紹的2PC,解決了其幾個缺點:

        • 1.「解決了協(xié)調者單點」,由主業(yè)務方發(fā)起并完成這個業(yè)務活動。業(yè)務活動管理器也變成多點,引入集群。
        • 2.「同步阻塞」:引入超時,超時后進行補償,并且不會鎖定整個資源,將資源轉換為業(yè)務邏輯形式,粒度變小。
        • 3.「數(shù)據(jù)一致性」,有了補償機制之后,由業(yè)務活動管理器控制一致性

        總之,TCC 就是通過代碼人為實現(xiàn)了兩階段提交,不同的業(yè)務場景所寫的代碼都不一樣,并且很大程度的「增加」了業(yè)務代碼的「復雜度」,因此,這種模式并不能很好地被復用。

        本地消息表


        執(zhí)行流程:


        • 消息生產(chǎn)方,需要額外建一個消息表,并「記錄消息發(fā)送狀態(tài)」。消息表和業(yè)務數(shù)據(jù)要在一個事務里提交,也就是說他們要在一個數(shù)據(jù)庫里面。然后消息會經(jīng)過MQ發(fā)送到消息的消費方。

          • 如果消息發(fā)送失敗,會進行重試發(fā)送。
        • 消息消費方,需要「處理」這個「消息」,并完成自己的業(yè)務邏輯。

          • 如果是「業(yè)務上面的失敗」,可以給生產(chǎn)方「發(fā)送一個業(yè)務補償消息」,通知生產(chǎn)方進行回滾等操作。
          • 此時如果本地事務處理成功,表明已經(jīng)處理成功了
          • 如果處理失敗,那么就會重試執(zhí)行。
        • 生產(chǎn)方和消費方定時掃描本地消息表,把還沒處理完成的消息或者失敗的消息再發(fā)送一遍。

        消息事務

        消息事務的原理是將兩個事務「通過消息中間件進行異步解耦」,和上述的本地消息表有點類似,但是是通過消息中間件的機制去做的,其本質就是'將本地消息表封裝到了消息中間件中'。

        執(zhí)行流程:

        • 發(fā)送prepare消息到消息中間件
        • 發(fā)送成功后,執(zhí)行本地事務
          • 如果事務執(zhí)行成功,則commit,消息中間件將消息下發(fā)至消費端
          • 如果事務執(zhí)行失敗,則回滾,消息中間件將這條prepare消息刪除
        • 消費端接收到消息進行消費,如果消費失敗,則不斷重試

        這種方案也是實現(xiàn)了「最終一致性」,對比本地消息表實現(xiàn)方案,不需要再建消息表,「不再依賴本地數(shù)據(jù)庫事務」了,所以這種方案更適用于高并發(fā)的場景。目前市面上實現(xiàn)該方案的「只有阿里的 RocketMQ」。

        最大努力通知

        最大努力通知的方案實現(xiàn)比較簡單,適用于一些最終一致性要求較低的業(yè)務。

        執(zhí)行流程:

        • 系統(tǒng) A 本地事務執(zhí)行完之后,發(fā)送個消息到 MQ;
        • 這里會有個專門消費 MQ 的服務,這個服務會消費 MQ 并調用系統(tǒng) B 的接口;
        • 要是系統(tǒng) B 執(zhí)行成功就 ok 了;要是系統(tǒng) B 執(zhí)行失敗了,那么最大努力通知服務就定時嘗試重新調用系統(tǒng) B, 反復 N 次,最后還是不行就放棄。

        Sagas 事務模型

        Saga事務模型又叫做長時間運行的事務

        其核心思想是「將長事務拆分為多個本地短事務」,由Saga事務協(xié)調器協(xié)調,如果正常結束那就正常完成,如果「某個步驟失敗,則根據(jù)相反順序一次調用補償操作」。

        Seata框架中一個分布式事務包含3種角色:

        「Transaction Coordinator (TC)」:事務協(xié)調器,維護全局事務的運行狀態(tài),負責協(xié)調并驅動全局事務的提交或回滾。「Transaction Manager (TM)」:控制全局事務的邊界,負責開啟一個全局事務,并最終發(fā)起全局提交或全局回滾的決議。「Resource Manager (RM)」:控制分支事務,負責分支注冊、狀態(tài)匯報,并接收事務協(xié)調器的指令,驅動分支(本地)事務的提交和回滾。

        seata框架「為每一個RM維護了一張UNDO_LOG表」,其中保存了每一次本地事務的回滾數(shù)據(jù)。

        具體流程:1.首先TM 向 TC 申請「開啟一個全局事務」,全局事務「創(chuàng)建」成功并生成一個「全局唯一的 XID」。

        2.XID 在微服務調用鏈路的上下文中傳播。

        3.RM 開始執(zhí)行這個分支事務,RM首先解析這條SQL語句,「生成對應的UNDO_LOG記錄」。下面是一條UNDO_LOG中的記錄,UNDO_LOG表中記錄了分支ID,全局事務ID,以及事務執(zhí)行的redo和undo數(shù)據(jù)以供二階段恢復。

        4.RM在同一個本地事務中「執(zhí)行業(yè)務SQL和UNDO_LOG數(shù)據(jù)的插入」。在提交這個本地事務前,RM會向TC「申請關于這條記錄的全局鎖」

        如果申請不到,則說明有其他事務也在對這條記錄進行操作,因此它會在一段時間內重試,重試失敗則回滾本地事務,并向TC匯報本地事務執(zhí)行失敗。

        6.RM在事務提交前,「申請到了相關記錄的全局鎖」,然后直接提交本地事務,并向TC「匯報本地事務執(zhí)行成功」。此時全局鎖并沒有釋放,全局鎖的釋放取決于二階段是提交命令還是回滾命令。

        7.TC根據(jù)所有的分支事務執(zhí)行結果,向RM「下發(fā)提交或回滾」命令。

        • RM如果「收到TC的提交命令」,首先「立即釋放」相關記錄的全局「鎖」,然后把提交請求放入一個異步任務的隊列中,馬上返回提交成功的結果給 TC。異步隊列中的提交請求真正執(zhí)行時,只是刪除相應 UNDO LOG 記錄而已。

        • RM如果「收到TC的回滾命令」,則會開啟一個本地事務,通過 XID 和 Branch ID 查找到相應的 UNDO LOG 記錄。將 UNDO LOG 中的后鏡與當前數(shù)據(jù)進行比較,

          • 如果不同,說明數(shù)據(jù)被當前全局事務之外的動作做了修改。這種情況,需要根據(jù)配置策略來做處理。
          • 如果相同,根據(jù) UNDO LOG 中的前鏡像和業(yè)務 SQL 的相關信息生成并執(zhí)行回滾的語句并執(zhí)行,然后提交本地事務達到回滾的目的,最后釋放相關記錄的全局鎖。

        總結

        本文介紹了分布式事務的一些基礎理論,并對常用的分布式事務方案進行了講解。

        分布式事務本身就是一個技術難題,業(yè)務中具體使用哪種方案還是需要不同的業(yè)務特點自行選擇,但是我們也會發(fā)現(xiàn),分布式事務會大大的提高流程的復雜度,會帶來很多額外的開銷工作,「代碼量上去了,業(yè)務復雜了,性能下跌了」。

        所以,當我們真實開發(fā)的過程中,能不使用分布式事務就不使用。

        1. Java 8 開發(fā)的 4 大頂級技巧,你都知道嗎 ?

        2. 想避免重復請求/并發(fā)請求?這樣處理才足夠優(yōu)雅

        3. GitHub 近兩萬 Star,無需編碼,可一鍵生成前后端代碼,這個開源項目有點強!

        4. 進程、線程、進程池、進程三態(tài)、同步、異步、并發(fā)、并行、串行

        最近面試BAT,整理一份面試資料Java面試BATJ通關手冊,覆蓋了Java核心技術、JVM、Java并發(fā)、SSM、微服務、數(shù)據(jù)庫、數(shù)據(jù)結構等等。

        獲取方式:點“在看”,關注公眾號并回復 Java 領取,更多內容陸續(xù)奉上。

        文章有幫助的話,在看,轉發(fā)吧。

        謝謝支持喲 (*^__^*)

        瀏覽 24
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

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

        手機掃一掃分享

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

          <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            一级a看片在线观看 | 成年免费A级毛片免费看无码 | 色色网站| 中国亚州精品历史女人久久 | 国产欧美一区二区精品性色超碰 | 高清无码18禁 | 女人脱精光让人桶爽了小说 | 国产激情福利 | 午夜三级做爰高潮 | 天天色色网 |