分布式事物的設(shè)計(jì)與實(shí)踐
點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號(hào)”
優(yōu)質(zhì)文章,第一時(shí)間送達(dá)
作者 | 彼岸舞
來源 | urlify.cn/VbaYNv
數(shù)據(jù)一致性定義
任何人
任何時(shí)間
任何地點(diǎn)
任何接入方式
任何服務(wù)
數(shù)據(jù)都是一致的
數(shù)據(jù)不一致產(chǎn)生的原因
數(shù)據(jù)分散在多處
多個(gè)DB
DB和緩存
二手交易平臺(tái)案例
用戶,交易,商品等功能
分布式事物產(chǎn)生的原因
剛開始是一個(gè)單體進(jìn)程

經(jīng)過演變,單體式服務(wù)演變成微服務(wù),每個(gè)服務(wù)都是單獨(dú)的進(jìn)程

在用戶請(qǐng)求量大的時(shí)候,為了緩解數(shù)據(jù)庫的壓力,添加了分布式緩存

分布式事物案例
電商平臺(tái)購買商品
下單->減庫存->支付

這就是分布式事物問題,當(dāng)APP要買東西,這個(gè)操作會(huì)涉及到多個(gè)服務(wù),意味著要操作多個(gè)數(shù)據(jù)庫,這樣本地事物就無法保證數(shù)據(jù)的一致性,所以就產(chǎn)生了分布式事物問題.
分布式事物場景
電商下單場景
下單
發(fā)送消息到MQ
一致性保證
本地事物
下單操作
發(fā)送MQ消息操作
放進(jìn)一個(gè)本地事物
上述做法有什么問題?

問題:如果發(fā)送消息超時(shí)了,你是不知道MQ的返回結(jié)果是成功和失敗的,,timeout這操作不是一個(gè)原子的
分布式事物分類
剛性分布式事物
強(qiáng)一致性
XA模型
CAP
CP
柔性分布式事物
最終一致性
CAP,BASE理論
AP
剛性分布式事物
滿足傳統(tǒng)事物特性
ACID( Atomicity-原子性, Consistency-一致性,Isolation-隔離性,Durability-持久性)
XA模型
XA是X/Open CAE Specification(Distributed Transaction Processing)模型中定義,XA規(guī)范由AP,RM,TM組成
其中應(yīng)用程序(Application Program簡稱AP),AP定義事物邊界(定義事物開始和結(jié)束)并訪問邊界事物內(nèi)的資源
資源管理器(Resource Manager簡稱RM),RM管理計(jì)算機(jī)共享的資源,資源及數(shù)據(jù)庫等
事物管理器(Transaction Manager,簡稱TM),負(fù)責(zé)管理全局事物,分配事物唯一標(biāo)識(shí),監(jiān)控事物的執(zhí)行進(jìn)度,并負(fù)責(zé)事物的提交,,回滾,失敗恢復(fù)等

2PC(兩階段提交-XA規(guī)范標(biāo)準(zhǔn)實(shí)現(xiàn))
案例
組織爬山
過程
二階段提交,是XA規(guī)范的標(biāo)準(zhǔn)實(shí)現(xiàn)
TM發(fā)起prepare投票
RM都同意后,TM再發(fā)起Commit
Commit 過程出現(xiàn)宕機(jī)等異常,節(jié)點(diǎn)服務(wù)重啟后,根據(jù)XA recover 再次進(jìn)行commit補(bǔ)償
缺點(diǎn)
同步阻塞模型
數(shù)據(jù)庫資源鎖定時(shí)間過長
全局鎖(隔離級(jí)別-串行化),并發(fā)低
不適合長事物場景

柔性分布式事物
CAP
分布式環(huán)境下P一定需要,CA權(quán)衡折中
BASE理論
Basically Available-基本可用
Soft state 柔性狀態(tài)
Eventual consistency 最終一致性
架構(gòu)思考
柔性事物是對(duì)XA協(xié)議的妥協(xié),他通過降低強(qiáng)一致性要求,從而降低數(shù)據(jù)庫資源鎖定時(shí)間,提升可用性
架構(gòu)經(jīng)典實(shí)現(xiàn)
TCC模型
Saga模型
TCC模型
Try-confirm-cancel
TCC模型完全交由業(yè)務(wù)實(shí)現(xiàn),每個(gè)子業(yè)務(wù)都需要實(shí)現(xiàn)Try-Confirm-cancel接口,對(duì)業(yè)務(wù)侵入大
資源鎖定交由業(yè)務(wù)方
try
嘗試執(zhí)行業(yè)務(wù),完成所有檢查,預(yù)留必要的業(yè)務(wù)資源
confirm
真正執(zhí)行業(yè)務(wù),不再做業(yè)務(wù)檢查
Cancel
釋放Try階段預(yù)留的業(yè)務(wù)資源
案例
匯款服務(wù),收款服務(wù)案例
A用戶向B用戶匯款500元
匯款服務(wù)
try
檢查A賬戶的有效性,及查看A賬戶的狀態(tài)是否為"轉(zhuǎn)賬中"或者"凍結(jié)"
檢查A賬戶余額是否充足
從A賬戶中扣減500元,并將狀態(tài)設(shè)置為轉(zhuǎn)賬中
預(yù)留扣減資源,將從A往B賬戶轉(zhuǎn)賬500元這個(gè)事件存入消息或者日志中
confirm
不做任何操作
cancel
A賬戶增加500元
從日志或者消息中,釋放扣減資源
收款服務(wù)
try
檢查B賬戶是否有效
confirm
讀取日志或消息,B賬戶增加500元
從日志或者消息中,釋放扣減資源
cancel
不做任何操作
Saga模型
起源于1987年Hector & Kenneth發(fā)表的論文Sagas
Saga模型把一個(gè)分布式事物拆分為多個(gè)本地事物,每個(gè)本地事物都有相應(yīng)的執(zhí)行模塊和補(bǔ)償模塊(對(duì)應(yīng)TCC中的confirm和cancel)
當(dāng)Saga事物中任意一個(gè)本地事物出錯(cuò)時(shí),可以通過調(diào)用相關(guān)的補(bǔ)償方法恢復(fù)之前的事物,到達(dá)事物最終一致性
當(dāng)每個(gè)Saga子事物T1,T2,....TN都有對(duì)應(yīng)的補(bǔ)償定義C1,C2,....CN-1,那么Saga系統(tǒng)可以保證
子事物序列T1,T2,.....TN得以完成(最佳情況)
或者序列T1,T2,...TJ,CJ-1,..., C2,C1,0<J<N,得以完成
Saga隔離性
業(yè)務(wù)層控制并發(fā)
在應(yīng)用層加鎖
應(yīng)用層預(yù)先凍結(jié)資源等
Saga恢復(fù)方式
向后恢復(fù),補(bǔ)償所有已完成的事物,如果任意子事物的失敗
向前恢復(fù),重試失敗的事物,假設(shè)每個(gè)子事物最終都會(huì)成功
剛性分布式事物VS柔性分布式事物
剛性事物(XA) | 柔性事物 | |
業(yè)務(wù)改造 | 無 | 有 |
回滾 | 支持 | 實(shí)現(xiàn)補(bǔ)償接口 |
一致性 | 強(qiáng)一致(CP) | 最終一致性(AP) |
隔離性 | 原生支持 | 實(shí)現(xiàn)資源鎖定接口 |
并發(fā)性能 | 嚴(yán)重衰退 | 略微衰退 |
適合場景 | 短事物,并發(fā)較低 | 長事物,高并發(fā) |
我們?nèi)绾螌?shí)踐
問題通用解決思路
解決這個(gè)問題本身
讓問題本身消失
圓珠筆筆芯漏油解決
圓珠筆筆芯在寫2W次就開始漏油,如果要解決這個(gè)問題本身,那么就是加入更好的材料,更高端的技術(shù),如果是讓問題本身消失呢,就是固定一個(gè)次數(shù),讓它只能寫1.5W次就沒油開始丟棄,這樣的兩種辦法
首選是讓問題本身消失,次選是解決這個(gè)問題本身
方案一:從業(yè)務(wù)場景消除分布式事物
思路:核心業(yè)務(wù)先處理,其他業(yè)務(wù)異步處理
方案二:柔性分布式事物
柔性分布式事物實(shí)踐
通用處理思路
本地事物-->短事物
分布式事物-->長事物
轉(zhuǎn)變成多個(gè)短事物
案例
A[下單]->B[減庫存]->C[支付]
A->DB1
B->DB2
C->DB3
A/B/C都成功
A/B成功,C失敗
補(bǔ)償
業(yè)務(wù)場景
異步場景
基于MQ消息驅(qū)動(dòng)分布事物
同步場景
基于異步補(bǔ)償分布
異步場景分布式事物設(shè)計(jì)
異步場景
商品交易
下單,支付

方案一:業(yè)務(wù)方提供本地操作成功回查功能
事物消息:MQ提供類似X/Open XA的分布式事物功能,通過MQ事物消息能達(dá)到分布式事物的最終一致
半消息:暫不投遞的消息,發(fā)送方已將消息成功發(fā)送到了MQ服務(wù)端,但是服務(wù)端未收到生產(chǎn)者對(duì)該消息的二次確認(rèn),此時(shí)該消息被標(biāo)記成"暫不能投遞"狀態(tài),處于該種狀態(tài)下的消息即半消息
消息回查:由于網(wǎng)絡(luò)閃斷,生產(chǎn)者應(yīng)用重啟等原因,導(dǎo)致某條事物消息的二次確認(rèn)丟失,MQ服務(wù)端通過掃描發(fā)現(xiàn)某條消息長期處于半消息時(shí),主要主動(dòng)向消息生產(chǎn)者詢問該消息的最終狀態(tài)(Commit或Rollback),即消息回查
MQ分布式事物設(shè)計(jì)方案

MQ分布式事物消息設(shè)計(jì)
MQ事物消息設(shè)計(jì)事物消息作為一種異步確保型事物,將兩個(gè)事物分支通過MQ進(jìn)行異步解耦,MQ事物消息的設(shè)計(jì)流程同樣借鑒了兩階段提交理論,整體交互流程如上圖
事物發(fā)起方首先發(fā)送prepare消息到MQ
在發(fā)送prepare消息成功后執(zhí)行本地事物
根據(jù)本地事物執(zhí)行結(jié)果返回commit或rollback
如果消息是rollback,MQ將刪除該prepare消息,不進(jìn)行下發(fā),如果是commit消息,MQ會(huì)將消息發(fā)送給consumer端
如果執(zhí)行本地事物過程中,執(zhí)行端掛掉,或者超時(shí),,MQ服務(wù)器端將不停的詢問producer來獲取事物狀態(tài)
consumer端的消費(fèi)成功機(jī)制有MQ保證
成本:
MQ需要支持半消息
MQ需要提供消息遍歷
業(yè)務(wù)方需要提供回查接口
業(yè)務(wù)方接入步驟

優(yōu)點(diǎn)
通用
缺點(diǎn)
業(yè)務(wù)方需要提供回查接口,對(duì)業(yè)務(wù)侵入大
發(fā)送消息非冪等
消費(fèi)端需要處理冪等
方案二:本地事物消息表
本地操作和發(fā)送消息通過本地事物強(qiáng)一致性
本地事物操作表
本地事物消息表
mqMessages(msgid,content,topic,status)

發(fā)送端消息不冪等
At least once (最少發(fā)一次)
Once Only (只發(fā)一次)
At more once(最多發(fā)一次)
消費(fèi)端處理消息冪等
分布式鎖
A->B->C
A/B成功,C失敗
記錄錯(cuò)誤日志
報(bào)警
人工介入
優(yōu)點(diǎn)
業(yè)務(wù)入侵小
相比于提供消息回查接口(RockectMQ)來說,實(shí)際異步場景還是本地消息事物表使用的比較多
同步場景分布式事物設(shè)計(jì)
同步場景
首頁推薦商品列表
商品信息
用戶信息
社交信息
購買商品
下單->A
減庫存->B
支付->C


通過業(yè)務(wù)邏輯層驅(qū)動(dòng)
解決方案
基于異步補(bǔ)償?shù)姆植际绞挛?/p>
架構(gòu)設(shè)計(jì)的三大關(guān)鍵點(diǎn)

開始記錄調(diào)用請(qǐng)求的參數(shù),如果失敗后基于參數(shù)做補(bǔ)償接口,接口需要保證冪等性
總體架構(gòu)設(shè)計(jì)

場景:A下單,B減庫存,C支付,在調(diào)用接口的時(shí)候,A先走Proxy存入事物ID,狀態(tài),參數(shù)等信息,然后執(zhí)行本地事物,接著B,C走同樣的流程如果都成功,那么事物狀態(tài)改成2,也就是成功,如果在C失敗的時(shí)候可以更具參數(shù),事物ID對(duì)A,B進(jìn)行補(bǔ)償
業(yè)務(wù)邏輯層Proxy設(shè)計(jì)(基于AOP實(shí)現(xiàn))
邏輯層調(diào)用上加上事物注解@Around("execution(**(..)) && @annotation(TX)")
Proxy在真正業(yè)務(wù)邏輯被調(diào)用之前,生成一個(gè)全局唯一TXID標(biāo)示事務(wù)組,TXID保存在ThreadLocal變量中,方法開始前寫入,完成后清除,并向遠(yuǎn)端數(shù)據(jù)庫寫入TXID并把事務(wù)組制成開始狀態(tài)
業(yè)務(wù)邏輯層調(diào)用數(shù)據(jù)訪問層之前,通過RPCProxy代理記錄,當(dāng)前調(diào)用請(qǐng)求參數(shù)
如果業(yè)務(wù)正常,調(diào)用完成后,當(dāng)前方法的調(diào)用記錄刪除或者存檔
如果業(yè)務(wù)異常,查詢調(diào)用鏈反向補(bǔ)償

數(shù)據(jù)訪問層設(shè)計(jì)
原子接口
補(bǔ)償接口
誰來提供?
業(yè)務(wù)方提供
冪等性保證
采用本地資源鎖,鎖定唯一資源
基于原則接口方法,在方法名加注解標(biāo)注補(bǔ)償方法名
@Compensable(cancelMethod = "cancelRecord")


分布式事物補(bǔ)償服務(wù)
事物組表(數(shù)據(jù)庫表TDB)
記錄事物組狀態(tài)
txid state timestamp
事物調(diào)用組表(數(shù)據(jù)庫表TDB)
記錄事物組內(nèi)的每一次調(diào)用,以及相關(guān)參數(shù)
txid actionid callmethod pramatype params
補(bǔ)償策略
調(diào)用執(zhí)行失敗,修改事物組狀態(tài)
分布式事物補(bǔ)償服務(wù)異步執(zhí)行補(bǔ)償
分布式事物成功案例
二手交易創(chuàng)建訂單事務(wù)組正常流程
鎖庫存->減紅包->創(chuàng)建訂單
代理層透明記錄調(diào)用請(qǐng)求參數(shù)
記錄事物域的開始與結(jié)束
在所有遠(yuǎn)程調(diào)用成功時(shí)
對(duì)業(yè)務(wù)邏輯不做侵入

分布式事物失敗案例
二手交易創(chuàng)建訂單事務(wù)組異常流程
微服務(wù)數(shù)據(jù)訪問層失敗,代理更改事務(wù)組狀態(tài)
微服務(wù)業(yè)務(wù)正常執(zhí)行
事物補(bǔ)償服務(wù)異步執(zhí)行補(bǔ)償

粉絲福利:Java從入門到入土學(xué)習(xí)路線圖
??????

??長按上方微信二維碼 2 秒
感謝點(diǎn)贊支持下哈 
