分散式事物的設計與實踐
- 2021 年 3 月 26 日
- 筆記
分散式事物設計與實踐
數據一致性定義
- 任何人
- 任何時間
- 任何地點
- 任何接入方式
- 任何服務
- 數據都是一致的
數據不一致產生的原因
- 數據分散在多處
- 多個DB
- DB和快取
- 二手交易平台案例
- 用戶,交易,商品等功能
分散式事物產生的原因
剛開始是一個單體進程
經過演變,單體式服務演變成微服務,每個服務都是單獨的進程
在用戶請求量大的時候,為了緩解資料庫的壓力,添加了分散式快取
分散式事物案例
電商平台購買商品
下單->減庫存->支付
這就是分散式事物問題,當APP要買東西,這個操作會涉及到多個服務,意味著要操作多個資料庫,這樣本地事物就無法保證數據的一致性,所以就產生了分散式事物問題.
分散式事物場景
- 電商下單場景
- 下單
- 發送消息到MQ
- 一致性保證
- 本地事物
- 下單操作
- 發送MQ消息操作
- 放進一個本地事物
上述做法有什麼問題?
問題:如果發送消息超時了,你是不知道MQ的返回結果是成功和失敗的,,timeout這操作不是一個原子的
分散式事物分類
- 剛性分散式事物
- 強一致性
- XA模型
- CAP
- CP
- 柔性分散式事物
- 最終一致性
- CAP,BASE理論
- AP
剛性分散式事物
滿足傳統事物特性
ACID( Atomicity-原子性, Consistency-一致性,Isolation-隔離性,Durability-持久性)
XA模型
- XA是X/Open CAE Specification(Distributed Transaction Processing)模型中定義,XA規範由AP,RM,TM組成
- 其中應用程式(Application Program簡稱AP),AP定義事物邊界(定義事物開始和結束)並訪問邊界事物內的資源
- 資源管理器(Resource Manager簡稱RM),RM管理電腦共享的資源,資源及資料庫等
- 事物管理器(Transaction Manager,簡稱TM),負責管理全局事物,分配事物唯一標識,監控事物的執行進度,並負責事物的提交,,回滾,失敗恢復等
2PC(兩階段提交-XA規範標準實現)
- 案例
- 組織爬山
- 過程
- 二階段提交,是XA規範的標準實現
- TM發起prepare投票
- RM都同意後,TM再發起Commit
- Commit 過程出現宕機等異常,節點服務重啟後,根據XA recover 再次進行commit補償
- 缺點
- 同步阻塞模型
- 資料庫資源鎖定時間過長
- 全局鎖(隔離級別-串列化),並發低
- 不適合長事物場景
柔性分散式事物
- CAP
- 分散式環境下P一定需要,CA權衡折中
- BASE理論
- Basically Available-基本可用
- Soft state 柔性狀態
- Eventual consistency 最終一致性
- 架構思考
- 柔性事物是對XA協議的妥協,他通過降低強一致性要求,從而降低資料庫資源鎖定時間,提升可用性
- 架構經典實現
- TCC模型
- Saga模型
TCC模型
- Try-confirm-cancel
- TCC模型完全交由業務實現,每個子業務都需要實現Try-Confirm-cancel介面,對業務侵入大
- 資源鎖定交由業務方
- try
- 嘗試執行業務,完成所有檢查,預留必要的業務資源
- confirm
- 真正執行業務,不再做業務檢查
- Cancel
- 釋放Try階段預留的業務資源
- 案例
- 匯款服務,收款服務案例
- A用戶向B用戶匯款500元
- 匯款服務
- try
- 檢查A賬戶的有效性,及查看A賬戶的狀態是否為”轉賬中”或者”凍結”
- 檢查A賬戶餘額是否充足
- 從A賬戶中扣減500元,並將狀態設置為轉賬中
- 預留扣減資源,將從A往B賬戶轉賬500元這個事件存入消息或者日誌中
- confirm
- 不做任何操作
- cancel
- A賬戶增加500元
- 從日誌或者消息中,釋放扣減資源
- 收款服務
- try
- 檢查B賬戶是否有效
- confirm
- 讀取日誌或消息,B賬戶增加500元
- 從日誌或者消息中,釋放扣減資源
- cancel
- 不做任何操作
Saga模型
- 起源於1987年Hector & Kenneth發表的論文Sagas
- Saga模型把一個分散式事物拆分為多個本地事物,每個本地事物都有相應的執行模組和補償模組(對應TCC中的confirm和cancel)
- 當Saga事物中任意一個本地事物出錯時,可以通過調用相關的補償方法恢復之前的事物,到達事物最終一致性
- 當每個Saga子事物T1,T2,….TN都有對應的補償定義C1,C2,….CN-1,那麼Saga系統可以保證
- 子事物序列T1,T2,…..TN得以完成(最佳情況)
- 或者序列T1,T2,…TJ,CJ-1,…, C2,C1,0<J<N,得以完成
- Saga隔離性
- 業務層控制並發
- 在應用層加鎖
- 應用層預先凍結資源等
- Saga恢復方式
- 向後恢復,補償所有已完成的事物,如果任意子事物的失敗
- 向前恢復,重試失敗的事物,假設每個子事物最終都會成功
剛性分散式事物VS柔性分散式事物
|
剛性事物(XA) |
柔性事物 |
業務改造 |
無 |
有 |
回滾 |
支援 |
實現補償介面 |
一致性 |
強一致(CP) |
最終一致性(AP) |
隔離性 |
原生支援 |
實現資源鎖定介面 |
並發性能 |
嚴重衰退 |
略微衰退 |
適合場景 |
短事物,並發較低 |
長事物,高並發 |
我們如何實踐
- 問題通用解決思路
- 解決這個問題本身
- 讓問題本身消失
- 圓珠筆筆芯漏油解決
- 圓珠筆筆芯在寫2W次就開始漏油,如果要解決這個問題本身,那麼就是加入更好的材料,更高端的技術,如果是讓問題本身消失呢,就是固定一個次數,讓它只能寫1.5W次就沒油開始丟棄,這樣的兩種辦法
- 首選是讓問題本身消失,次選是解決這個問題本身
- 方案一:從業務場景消除分散式事物
- 思路:核心業務先處理,其他業務非同步處理
- 方案二:柔性分散式事物
柔性分散式事物實踐
- 通用處理思路
- 本地事物–>短事物
- 分散式事物–>長事物
- 轉變成多個短事物
- 案例
- A[下單]->B[減庫存]->C[支付]
- A->DB1
- B->DB2
- C->DB3
- A/B/C都成功
- A/B成功,C失敗
- 補償
- 業務場景
- 非同步場景
- 基於MQ消息驅動分布事物
- 同步場景
- 基於非同步補償分布
非同步場景分散式事物設計
非同步場景
商品交易
下單,支付
方案一:業務方提供本地操作成功回查功能
- 事物消息:MQ提供類似X/Open XA的分散式事物功能,通過MQ事物消息能達到分散式事物的最終一致
- 半消息:暫不投遞的消息,發送方已將消息成功發送到了MQ服務端,但是服務端未收到生產者對該消息的二次確認,此時該消息被標記成”暫不能投遞”狀態,處於該種狀態下的消息即半消息
- 消息回查:由於網路閃斷,生產者應用重啟等原因,導致某條事物消息的二次確認丟失,MQ服務端通過掃描發現某條消息長期處於半消息時,主要主動向消息生產者詢問該消息的最終狀態(Commit或Rollback),即消息回查
- MQ分散式事物設計方案
- MQ分散式事物消息設計
- MQ事物消息設計事物消息作為一種非同步確保型事物,將兩個事物分支通過MQ進行非同步解耦,MQ事物消息的設計流程同樣借鑒了兩階段提交理論,整體交互流程如上圖
- 事物發起方首先發送prepare消息到MQ
- 在發送prepare消息成功後執行本地事物
- 根據本地事物執行結果返回commit或rollback
- 如果消息是rollback,MQ將刪除該prepare消息,不進行下發,如果是commit消息,MQ會將消息發送給consumer端
- 如果執行本地事物過程中,執行端掛掉,或者超時,,MQ伺服器端將不停的詢問producer來獲取事物狀態
- consumer端的消費成功機制有MQ保證
- 成本:
- MQ需要支援半消息
- MQ需要提供消息遍歷
- 業務方需要提供回查介面
- 業務方接入步驟
- 優點
- 通用
- 缺點
- 業務方需要提供回查介面,對業務侵入大
- 發送消息非冪等
- 消費端需要處理冪等
方案二:本地事物消息表
- 本地操作和發送消息通過本地事物強一致性
- 本地事物操作表
- 本地事物消息表
- mqMessages(msgid,content,topic,status)
- 發送端消息不冪等
- At least once (最少發一次)
- Once Only (只發一次)
- At more once(最多發一次)
- 消費端處理消息冪等
- 分散式鎖
- A->B->C
- A/B成功,C失敗
- 記錄錯誤日誌
- 報警
- 人工介入
- 優點
- 業務入侵小
相比於提供消息回查介面(RockectMQ)來說,實際非同步場景還是本地消息事物表使用的比較多
同步場景分散式事物設計
- 同步場景
- 首頁推薦商品列表
- 商品資訊
- 用戶資訊
- 社交資訊
- 購買商品
- 下單->A
- 減庫存->B
- 支付->C
通過業務邏輯層驅動
- 解決方案
- 基於非同步補償的分散式事物
- 架構設計的三大關鍵點
開始記錄調用請求的參數,如果失敗後基於參數做補償介面,介面需要保證冪等性
- 總體架構設計
場景:A下單,B減庫存,C支付,在調用介面的時候,A先走Proxy存入事物ID,狀態,參數等資訊,然後執行本地事物,接著B,C走同樣的流程如果都成功,那麼事物狀態改成2,也就是成功,如果在C失敗的時候可以更具參數,事物ID對A,B進行補償
業務邏輯層Proxy設計(基於AOP實現)
- 邏輯層調用上加上事物註解@Around(“execution(**(..)) && @annotation(TX)”)
- Proxy在真正業務邏輯被調用之前,生成一個全局唯一TXID標示事務組,TXID保存在ThreadLocal變數中,方法開始前寫入,完成後清除,並向遠端資料庫寫入TXID並把事務組製成開始狀態
- 業務邏輯層調用數據訪問層之前,通過RPCProxy代理記錄,當前調用請求參數
- 如果業務正常,調用完成後,當前方法的調用記錄刪除或者存檔
- 如果業務異常,查詢調用鏈反向補償
- 數據訪問層設計
- 原子介面
- 補償介面
- 誰來提供?
- 業務方提供
- 冪等性保證
- 採用本地資源鎖,鎖定唯一資源
- 基於原則介面方法,在方法名加註解標註補償方法名
- @Compensable(cancelMethod = “cancelRecord”)
- 分散式事物補償服務
- 事物組表(資料庫表TDB)
- 記錄事物組狀態
- txid state timestamp
- 事物調用組表(資料庫表TDB)
- 記錄事物組內的每一次調用,以及相關參數
- txid actionid callmethod pramatype params
- 補償策略
- 調用執行失敗,修改事物組狀態
- 分散式事物補償服務非同步執行補償
分散式事物成功案例
- 二手交易創建訂單事務組正常流程
- 鎖庫存->減紅包->創建訂單
- 代理層透明記錄調用請求參數
- 記錄事物域的開始與結束
- 在所有遠程調用成功時
- 對業務邏輯不做侵入
分散式事物失敗案例
- 二手交易創建訂單事務組異常流程
- 微服務數據訪問層失敗,代理更改事務組狀態
- 微服務業務正常執行
- 事物補償服務非同步執行補償
好了,到這裡分散式事物也就寫完了..休息一下,,哎,又到了找工作的時候了,有需要可以聯繫我