1. 程式人生 > >分散式事物的設計與實踐

分散式事物的設計與實踐

分散式事物設計與實踐

資料一致性定義

  • 任何人
  • 任何時間
  • 任何地點
  • 任何接入方式
  • 任何服務
  • 資料都是一致的

資料不一致產生的原因

  • 資料分散在多處
    • 多個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事物訊息的設計流程同樣借鑑了兩階段提交理論,整體互動流程如上圖
    1. 事物發起方首先發送prepare訊息到MQ
    2. 在傳送prepare訊息成功後執行本地事物
    3. 根據本地事物執行結果返回commit或rollback
    4. 如果訊息是rollback,MQ將刪除該prepare訊息,不進行下發,如果是commit訊息,MQ會將訊息傳送給consumer端
    5. 如果執行本地事物過程中,執行端掛掉,或者超時,,MQ伺服器端將不停的詢問producer來獲取事物狀態
    6. 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
    • 補償策略
      • 呼叫執行失敗,修改事物組狀態
      • 分散式事物補償服務非同步執行補償

分散式事物成功案例

  • 二手交易建立訂單事務組正常流程
    • 鎖庫存->減紅包->建立訂單
  • 代理層透明記錄呼叫請求引數
    • 記錄事物域的開始與結束
    • 在所有遠端呼叫成功時
    • 對業務邏輯不做侵入

分散式事物失敗案例

  • 二手交易建立訂單事務組異常流程
    • 微服務資料訪問層失敗,代理更改事務組狀態
    • 微服務業務正常執行
    • 事物補償服務非同步執行補償

好了,到這裡分散式事物也就寫完了..休息一下,,哎,又到了找工作的時候了,有需要可以聯絡我