學習思路
-
ACID解釋
-
本地事務
-
分佈式事務解決方案及優缺點
-
生產最常用的方案
一、ACID-本地事務
學習事務的前提就是要了解什麼是ACID(事務的四大特性)
-
Atomicity(原子性):一個事務的執行過程就是一個原子操作,要麼全部成功,要麼全部失敗,不會出現其它情況
-
Consistency(一致性):數據執行前後,數據的完整性必須是一致的,比如轉賬A+1;B-1但總數還是不變的
-
Isolation(隔離性):多個用戶併發執行事務,每個事務是相互隔離的,不會產生任何影響
-
Durability(持久性):事務一旦提交,數據便永久的存儲在了數據庫,不受外界條件影響
只用滿足了以上4大特性纔算一個完整的事務
二、本地事務
系統發展初期,單體應用對應一個數據庫,本地操作數據我們很容易就實現了ACID,主要流程:開啓事務-->操作數據 -->提交/回滾事務;實際使用中主要有:編程式事務、聲明式事務
-
編程式事務:主要通過代碼編寫自己手動實現,開發一般不用
-
聲明式事務:主要通過使用AOP攔截方法在方法執行前後分別開始事務,提交/回滾事務;實際使用主要通過配置文件配置或者@Transaction註解實現
三、分佈式事務解決方案及優缺點
隨着系統拆分到數據庫拆分,我們的一個操作需要跨多個系統跨多個數據庫才能完成,然而多個數據庫協同操作也就保證不了ACID四大特性
爲了解決問題XA協議來了
1、XA協議:
由之前的Application控制一個ResourceManager變成了:一個TransactionManager管理多個本地ResourceManager協同完成一次事務
基於XA協議從而產出了後面的兩階段提交和三階段提交
2、兩階段提交(2pc)
-
TM(事務管理器)分別向多個RM(本地資源管理器)發送準備提交指令(如果此時一方發送失敗,表示該事務失敗);RM執行完本地業務後分別響應TM(如果此時一方返回NO或者異常表示事務失敗)
-
如果RM返回成功;TM分別向RM發送提交指令;如果RM返回失敗;TM分別向RM發送回滾指令
缺點:
-
如果在準備階段如果其中一個RM掛掉了,其它RM就不應該走,而不是回滾
-
TM單點問題:如果TM掛掉會導致整個服務不可用
-
同步阻塞問題:準備階段需要等待所有準備結果,如果其中一步沒有返回結果將會一直阻塞等待
-
數據不一致問題:如果發送1個RM後TM掛掉將會導致其它RM跟這個數據不一致
3、三階段提交(3pc)
針對兩階段的缺點產生了三階段
-
TM向RM發送真正的準備請求時先發送can_commit請求驗證RM是否正常(如果有RM異常直接返回事務失敗)
-
如果全部正常:TM向RM發送pre_commit(準備)請求,RM執行操作返回操作結果
-
如果第二步返回成功:TM向RM發送提交通知;如果返回失敗發送回滾通知
缺點:
-
TM單點問題
-
如果在最後一步TM發送的是回滾通知,但其中有部分RM沒有接受到指令那麼會超時提交,出現數據不一致的情況
2pc和3pc的對比
-
can_commit
-
2pc:直接進入準備階段執行業務,如果有RM宕機還要回滾其它的
-
3pc新增can_commit操作驗證RM是否正常
-
-
同步阻塞:
-
2pc在準備過程中如果其中一個RM阻塞,其它RM都要處於阻塞等待中
-
3pc中新增的超時機制,防止無限阻塞
-
-
自動提交機制:
-
2pc:TM發送RM提交或回滾無響應會進入阻塞
-
3pc:如果RM沒有接受到TM的任何提交或回滾請求,超時後自動提交(大部分機率超時的請求基本上都是成功)
-
面對2pc和3pc的這麼多缺點,所有實際開發中這兩種基本不用
4、TCC機制
解釋:
-
主業務數據庫向下遊業務庫發送Try操作(直接執行本地事務,但數據狀態不是終態,而是一箇中間狀態(比如凍結))
-
當所有從業務庫返回成功後,主業務發送confirm請求,從業務執行confirm方法(將中間狀態改爲最終成功狀態)
-
當部分返回失敗後:主業務發送cancel請求,從業務執行cancel方法(將中間狀態改爲失敗,恢復Try操作之前的狀態)
優點:
-
各個子系統自己控制事務,不存在事務阻塞等待問題
-
各個系統都是集羣部署,不存在單點問題
-
一致性得到保障
缺點:
-
各個系統需要實現try、confirm、cancel的相關邏輯,代碼侵入性高
-
如果confirm或者cancel執行失敗需要定時任務實現最終一致
題外話:此種方法有沒有感覺很熟悉,我們實際開發中其實不自覺的會用到一些大同小異的補償機制,思想都一樣
5、基於MQ實現最終一致性
聽起來很高大尚,實現很簡單、、、
我們這裏最好使用支持事務消息的MQ比如RocketMQ,不支持事務的MQ實現略顯麻煩,但原理一樣(基於RocketMQ的點擊:RocketMQ事務消息學習)
我們這裏主要學習一下不支持事務的MQ
不管支不支持事務,我們要實現的主要思路:
-
使發送MQ消息和執行本地事務兩個操作放入一個原子操作(放入一個事務)
-
消費失敗的消息,利用MQ的重試機制或者記錄消息數據利用定時任務重試消費,到達最終一致性
第一步你可能會疑問:我將發送MQ消息和放入本地事務就可以了;這樣大部分情況沒問題,但發送MQ是遠程調用如果網絡異常發生超時會產生大量的長事務,拖垮系統
研發思路:
-
新建發送消息表(記錄發送的消息)
-
不直接對接MQ發送消息,而是將消息入庫,把消息入庫和本地業務放入一個本地事務,確保本地業務和消息同時成功
-
定時任務掃發送消息庫,發送消息,發送成功刪除消息(可批量查詢)
-
新建消費消息表(記錄發送到消費者消息)
-
當MQ重試到一定次數之後可利用MQ的死信隊列或者將消息存入消費消息表(一般這種長時間錯誤都是數據異常,可配合報警人工介入)
-
定時任務掃表消費消息,消費成功刪除消息(可批量查詢)
注意:所有的MQ消費接口,存入表操作一定要做冪等
四、生產最常用的方案
-
狀態類業務可按狀態機實現狀態控制用TCC思想+定時任務做補償
-
通知類業務、耗時比較長的新增業務,可引入MQ,可解耦、緩衝峯值、做最終一致
-
但很多情況下我們都不用事務、、、90%的重點業務、容易出錯的業務通過定時任務做補償就能解決:補償一定次數我們報警人工介入
公衆號主要記錄各種源碼、面試題、微服務技術棧,幫忙關注一波,非常感謝