目錄
- 什麼是分布式事務
- 常用的分布式事務解決方案
-
- `2PC`
-
- `2PC` 的優缺點
-
- 優點
- 缺點
- `3PC`
-
- 我們先來看一下 `3PC` 的階段變更有什麼影響
- 我們再來看下參與者逾時機制能帶來什麼樣的影響
- `TCC`
-
- `TCC` 的注意點
- 本地消息表
- 基于 `RocketMQ` 的消息事務
什麼是分布式事務
衆所周知,資料庫能實作本地事務,也就是在同一個資料庫中,你可以允許一組操作要麼全都正确執行,要麼全都不執行。這裡特别強調了本地事務,也就是目前的資料庫隻能支援同一個資料庫中的事務。但現在的系統往往采用分布式的微服務架構,每個系統應用都擁有獨立的資料庫,是以就出現了跨多個資料庫的事務需求,這種事務即為分布式事務
那麼在目前資料庫不支援跨庫事務的情況下,我們應該如何實作分布式事務呢?本文會為大家介紹幾種目前常用的分布式事務解決方案
常用的分布式事務解決方案
-
2PC
-
3PC
-
TCC
- 本地消息表
- 基于
的消息事務RacketMQ
2PC
2PC
2PC
即兩階段送出,兩階段送出是一種強一緻性設計,
2PC
引入了一個事務協調者的角色來協調管理各參與者的送出和復原,兩階段分别指的是準備階段和送出階段
我們先來看看第一個階段,即準備階段
準備階段
- 事務協調者會向各參與者發送準備指令,你可以把準備指令了解成除了送出事務之外啥事都做完了
- 事務協調者會同步等待所有的參與者響應之後就進入第二階段即送出階段(注意送出階段不一定是送出事務,也可能是復原事務)
假如在第一階段所有的參與者都傳回準備成功,那麼事務協調者則向所有的參與者發送送出事務指令,然後等待所有的事務都送出成功之後,傳回事務執行成功
假如在第一階段有任何一個參與者傳回準備失敗,那麼事務協調者就會向所有的參與者發送復原事務的請求,即分布式無事執行失敗
我們再來看看第二個階段,即送出階段
假如在第一階段所有的參與者都傳回準備成功,那麼在第二階段事務送出失敗怎麼辦呢
- 第二階段如果執行的是復原事務操作,那麼答案是不斷重試,直到所有參與者都復原了,不然那些在第一階段準備成功的參與者會一直阻塞着
- 第二階段如果執行的是送出事務操作,那麼答案也是不斷重試,因為有可能一些參與者的事務已經送出成功了,這個時候隻有一條路,不斷的重試,直到送出成功,到最後真的不行隻能人工介入處理
2PC
的優缺點
2PC
優點
能利用資料庫自身的功能進行本地事務的送出和復原,也就是說送出和復原實際操作不需要我們實作,不侵入業務邏輯由資料庫完成
缺點
- 同步阻塞:可以看到在第一階段執行了準備指令後,所有的參與者都處于同步等待鎖定狀态,因為除了事務的送出之外啥都做了。是以這時候如果本地的其他請求要通路同一個資源,比如要修改商品表
等于id
的那條資料,那麼此時是被阻塞住的。必須等待前面事務的完結,收到送出/復原指令執行完釋放資源後,這個請求才能得以繼續。是以假設這個分布式事務涉及到很多參與者,然後有些參與者處理又特别複雜特别慢,那麼那些處理快的節點也得等着。是以說效率有點低100
- 單點故障:可以看到這個單點就是事務協調者,如果事務協調者挂了整個事務就執行不下去了。如果協調者在發送準備指令前挂了還行,畢竟每個資源都還未執行指令資源是沒被鎖定的。可怕的是在發送完準備指令之後挂了。這時候每個本地資源都執行完處于鎖定狀态了,都杵着了,這就很僵硬了
- 資料不一緻:因為事務協調者和參與者之間的交流是經過網絡的,而網絡有時候會抽風或者發生局部網絡異常。這可能導緻某些參與者無法收到協調者的請求而其他參與者收到了請求,例如送出請求。然後那些收到指令的參與者就送出事務了,此時就産生了資料不一緻的問題
總結:
2PC
是一種盡量保證強一緻性的分布式事務,是以它是同步阻塞的,而同步阻塞就導緻長久的資源鎖定問題,總體而言效率低,并且存在單點故障問題,在極端條件下存在資料不一緻的風險
3PC
3PC
3PC
的出現是為了解決
2PC
的一些問題,相比于
2PC
它在參與者中也引入了逾時機制,并且新增了一個階段使得參與者可以利用這一個階段統一各自的狀态
3PC
包含了三個階段:準備階段,預送出階段,送出階段。準備階段單純就是事務協調者去通路參與者,類似于你還好嗎,能不能接受請求?預送出其實就是
2PC
的準備階段,除了事務的送出啥都幹了。送出階段和
2PC
的送出一緻
在
3PC
中,不管哪一個階段有參與者傳回失敗,那麼都會宣布事務失敗。這和
2PC
是一樣的(當然到最後的送出階段和
2PC
一樣隻要是送出請求就隻能不斷重試)
我們先來看一下 3PC
的階段變更有什麼影響
3PC
- 首先準備階段變更為不會直接指向事務,而是先去詢問所有的參與者是否有條件接受這個事務。是以不會一來就幹活直接鎖定資源,使得在某些資源不可用的情況下所有參與者都同步阻塞着
-
多了一個階段其實就是在執行事務之前來确認所有的參與者是否正常,防止個别參與者不正常的情況下,其他的參與者都執行了事務、鎖定了資源3PC
我們再來看下參與者逾時機制能帶來什麼樣的影響
- 我們知道
是同步阻塞的,上面我們已經分析了事務協調者挂在了送出請求還未發出去的時候是最傷的,所有參與者都已經鎖定了資源并且同步阻塞等待着。那麼引入了逾時機制,參與者就不會傻等了,如果是等待送出指令逾時,那麼參與者就會送出事務了,因為都到了這一階段了大機率是送出的,如果是等待預送出指令逾時,那該幹啥就幹啥了,反正本來啥也沒幹2PC
- 然而逾時機制也會帶來資料不一緻的問題,比如在等待送出指令時候逾時了,參與者預設執行的是送出事務操作,但是有可能執行的是復原操作,這樣一來資料就不一緻了
總結:
3PC
相對于
2PC
做了一定的改進:引入了參與者逾時機制,并且增加了預送出階段使得故障恢複之後協調者的決策複雜度降低,但整體的互動過程更長了,性能有所下降,并且還是會存在資料不一緻問題。是以
2PC
和
3PC
都不能保證資料
100%
一緻,是以一般都需要有定時掃描補償機制。我再說下
3PC
我沒有找到具體的實作,是以我認為
3PC
隻是純的理論上的東西,而且可以看到相比于
2PC
它是做了一些努力但是效果甚微,是以隻做了解即可
TCC
TCC
2PC
和
3PC
都是資料庫層面的,而
TCC
是業務層面的分布式事務
TCC
指的是
Try - Confirm - Cancel
,也就是業務層面需要寫對應的三個方法。其實從思想上看和
2PC
差不多,都是先試探性的執行,如果都可以那就真正的執行,如果不行就復原
-
指的是預留,即資源的預留和鎖定,注意是預留Try
-
指的是确認操作,這一步其實就是真正的執行了Confirm
-
指的是撤銷操作,可以了解為把預留階段的動作撤銷了Cancel
雖說對業務有侵入,但是
TCC
沒有資源的阻塞,每一個方法都是直接送出事務的,如果出錯是通過業務層面的
Cancel
來進行補償,是以也稱補償性事務方法
如果是所有人
Try
都成功了,都執行
Confirm
了,但是個别
Confirm
失敗了怎麼辦
- 這時候隻能是不停地重試調用失敗了的
直到成功為止,如果真的不行隻能記錄下來,到時候人工介入了Confirm
TCC
的注意點
TCC
- 幂等問題:因為網絡調用無法保證請求一定能到達,是以都會有重調機制,是以對于
三個方法都需要幂等實作,避免重複執行産生錯誤Try、Confirm、Cancel
- 空復原問題:指的是
方法由于網絡問題沒收到逾時了,此時事務管理器就會發出Try
指令,那麼需要支援Cancel
在未執行Cancel
的情況下能正常的Try
Cancel
- 懸挂問題:這個問題也是指
方法由于網絡阻塞逾時觸發了事務管理器發出了Try
指令,但是執行了Cancel
指令之後Cancel
請求到了Try
總結:相對于
2PC、3PC
,
TCC
适用的範圍更大,但是開發量也更大,畢竟都在業務上實作。它是業界比較常用的分布式事務實作方式,而且從變體也可以得知,還是得看業務變通的,不是說你要用
TCC
一定就得死闆的讓所有的服務都改造成那三個方法
本地消息表
本地消息就是利用了本地事務,會在資料庫中存放一張本地事務消息表,在進行本地事務操作中加入了本地消息的插入,即将業務的執行和将消息放入消息表中的操作放在同一個事務中送出。這樣本地事務執行成功的話,消息肯定也插入成功,然後再調用其他服務,如果調用成功就修改這條本地消息的狀态。如果失敗也不要緊,會有一個背景線程掃描,發現這些狀态的消息,會一直調用相應的服務,一般會設定重試的次數,如果一直不行則特殊記錄,等待人工介入處理
基于 RocketMQ
的消息事務
RocketMQ
RocketMQ
就很好的支援了消息事務,我們來看一下如何通過消息實作事務
- 第一步先給
發送事務消息即半消息,半消息不是說一半消息,而是這個消息對消費者來說不可見,然後發送成功後發送方再執行本地事務Broker
- 再根據本地事務的結果向
發送Broker
或者Commit
指令RollBack
- 并且
的發送方會提供一個反查事務狀态接口,如果一段時間内半消息沒有收到任何操作請求,那麼RocketMQ
會通過反查接口得知發送方事務是否執行成功,然後執行Broker
或者Commit
指令RollBack
- 如果是
那麼訂閱方就能收到這條消息,然後再做對應的操作,做完了之後再消費這條消息即可Commit
- 如果是
那麼訂閱方收不到這條消息,等于事務就沒執行過RollBack
可以看到通過
RocketMQ
還是比較容易實作的,
RocketMQ
提供了事務消息的功能,隻需要定義好事務反查接口即可