大家好,我是冰河~~
今天,咱們就暫時不聊【精通高并發(fā)系列】了验游,今天插播一下分布式事務(wù),為啥保檐?因為冰河聯(lián)合貓大人共同創(chuàng)作的分布式事務(wù)領(lǐng)域的開山之作——《深入理解分布式事務(wù):原理與實戰(zhàn)》一書正式出版了耕蝉,于2021年10月20日開始在當(dāng)當(dāng)預(yù)售,當(dāng)天即登上當(dāng)當(dāng)新書榜第一的位置夜只!
本地事務(wù)
本地事務(wù)流程
在介紹分布式事務(wù)之前垒在,我們先來看看本地事務(wù)。首先盐肃,我們先來一張圖爪膊。
由上圖,我們可以看出砸王,本地事務(wù)由資源管理器(比如DBMS,數(shù)據(jù)庫管理系統(tǒng))在本地進(jìn)行管理峦阁。
本地事務(wù)的優(yōu)缺點
本地事務(wù)具備相應(yīng)的優(yōu)點谦铃,也有其不足。
優(yōu)點:
支持嚴(yán)格的ACID屬性榔昔。
可靠驹闰,事務(wù)實現(xiàn)的效率高(只是在本地操作)。
可以只在RM(資源管理器)中操作事務(wù)撒会。
編程模型簡單嘹朗。
缺點:
缺乏分布式事務(wù)的處理能力。
數(shù)據(jù)隔離的最小單元由RM(資源管理器決定)诵肛,開發(fā)人員無法決定數(shù)據(jù)隔離的最小單元屹培。比如:數(shù)據(jù)庫中的一條記錄等。
ACID屬性
說起事務(wù),我們不得不提的就是事務(wù)的ACID屬性褪秀。A(Atomic):原子性蓄诽,構(gòu)成事務(wù)的所有操作,要么都執(zhí)行完成媒吗,要么全部不執(zhí)行仑氛,不可能出現(xiàn)部分成功部分失 敗的情況。
C(Consistency):一致性闸英,在事務(wù)執(zhí)行前后锯岖,數(shù)據(jù)庫的一致性約束沒有被破壞。比如:張三向李四轉(zhuǎn)100元甫何, 轉(zhuǎn)賬前和轉(zhuǎn)賬后的數(shù)據(jù)的正確狀態(tài)叫作一致性嚎莉,如果出現(xiàn)張三轉(zhuǎn)出100元,李四賬戶沒有增加100元這就出現(xiàn)了數(shù) 據(jù)錯誤沛豌,就沒有達(dá)到一致性趋箩。
I(Isolation):隔離性,數(shù)據(jù)庫中的事務(wù)一般都是并發(fā)的加派,隔離性是指并發(fā)的兩個事務(wù)的執(zhí)行互不干擾叫确,一個事 務(wù)不能看到其他事務(wù)運(yùn)行過程的中間狀態(tài)。通過配置事務(wù)隔離級別可以避臟讀芍锦、重復(fù)讀等問題竹勉。
D(Durability):持久性,事務(wù)完成之后娄琉,該事務(wù)對數(shù)據(jù)的更改會被持久化到數(shù)據(jù)庫次乓,且不會被回滾。
分布式事務(wù)
隨著業(yè)務(wù)的快速發(fā)展孽水,網(wǎng)站系統(tǒng)往往由單體架構(gòu)逐漸演變?yōu)榉植际狡毖⑽⒎?wù)架構(gòu),而對于數(shù)據(jù)庫則由單機(jī)數(shù)據(jù)庫架構(gòu)向分布式數(shù)據(jù)庫架構(gòu)轉(zhuǎn)變女气。此時杏慰,我們會將一個大的應(yīng)用系統(tǒng)拆分為多個可以獨立部署的應(yīng)用服務(wù),需要各個服務(wù)之間進(jìn)行遠(yuǎn)程協(xié)作才能完成事務(wù)操作炼鞠。
我們可以使用下圖來表示剛開始我們系統(tǒng)的單體架構(gòu)缘滥。
上圖中,我們將同一個項目中的不同模塊組織成不同的包來進(jìn)行管理谒主,所有的程序代碼仍然是放在同一個項目中朝扼。
后續(xù)由于業(yè)務(wù)的發(fā)展,我們將其擴(kuò)展為分布式霎肯、微服務(wù)架構(gòu)擎颖。此時榛斯,我們將一個大的項目拆分為一個個小的可以獨立部署的微服務(wù),每個微服務(wù)都有自己的數(shù)據(jù)庫肠仪,如下所示肖抱。
又比如,在我們的程序中异旧,經(jīng)常會在同一個事務(wù)中執(zhí)行類似如下的代碼來完成我們的需求意述。
<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" cid="n51" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;" lang="java">@Transactional(rollbackFor = Exception.class)
public void submitOrder() {
orderDao.update(); // 更新訂單信息
accountService.update(); // 修改資金賬戶的金額
pointService.update(); // 修改積分
accountingService.insert(); // 插入交易流水
merchantNotifyService.notify(); // 通知支付結(jié)果
}</pre>
上述代碼中的業(yè)務(wù),僅僅在submitOrder()方法上添加了一個@Transactional注解吮蛹,這能夠在分布式場景下避免分布式事務(wù)的問題嗎荤崇?很顯然是不行的。
如果上述代碼所對應(yīng)的:訂單信息潮针、資金賬戶信息术荤、積分信息、交易流水等信息分別存儲在不同的數(shù)據(jù)里每篷,而支付完成后瓣戚,通知的目標(biāo)系統(tǒng)的數(shù)據(jù)同樣是存儲在不同的數(shù)據(jù)庫中。此時就會產(chǎn)生分布式事務(wù)問題焦读。
分布式事務(wù)產(chǎn)生的場景
跨JVM進(jìn)程
當(dāng)我們將單體項目拆分為分布式子库、微服務(wù)項目之后,各個服務(wù)之間通過遠(yuǎn)程REST或者RPC調(diào)用來協(xié)同完成業(yè)務(wù)操作矗晃。典型的場景就是:商城系統(tǒng)中的訂單微服務(wù)和庫存微服務(wù)仑嗅,用戶在下單時會訪問訂單微服務(wù),訂單微服務(wù)在生成訂單記錄時张症,會調(diào)用庫存微服務(wù)來扣減庫存仓技。各個微服務(wù)是部署在不同的JVM進(jìn)程中的,此時俗他,就會產(chǎn)生因跨JVM進(jìn)程而導(dǎo)致的分布式事務(wù)問題脖捻。
跨數(shù)據(jù)庫實例
單體系統(tǒng)訪問多個數(shù)據(jù)庫實例,也就是跨數(shù)據(jù)源訪問時會產(chǎn)生分布式事務(wù)拯辙。例如郭变,我們的系統(tǒng)中的訂單數(shù)據(jù)庫和交易數(shù)據(jù)庫是放在不同的數(shù)據(jù)庫實例中,當(dāng)用戶發(fā)起退款時涯保,會同時操作用戶的訂單數(shù)據(jù)庫和交易數(shù)據(jù)庫,在交易數(shù)據(jù)庫中執(zhí)行退款操作周伦,在訂單數(shù)據(jù)庫中將訂單的狀態(tài)變更為已退款夕春。由于數(shù)據(jù)分布在不同的數(shù)據(jù)庫實例,需要通過不同的數(shù)據(jù)庫連接會話來操作數(shù)據(jù)庫中的數(shù)據(jù)专挪,此時及志,就產(chǎn)生了分布式事務(wù)片排。
多服務(wù)單數(shù)據(jù)庫
多個微服務(wù)訪問同一個數(shù)據(jù)庫。例如速侈,訂單微服務(wù)和庫存微服務(wù)訪問同一個數(shù)據(jù)庫也會產(chǎn)生分布式事務(wù)率寡,原因是:多個微服務(wù)訪問同一個數(shù)據(jù)庫,本質(zhì)上也是通過不同的數(shù)據(jù)庫會話來操作數(shù)據(jù)庫倚搬,此時就會產(chǎn)生分布式事務(wù)冶共。
注意:跨數(shù)據(jù)庫實例場景和多服務(wù)單數(shù)據(jù)庫場景,本質(zhì)上都是因為會產(chǎn)生不同的數(shù)據(jù)庫會話來操作數(shù)據(jù)庫中的數(shù)據(jù)每界,進(jìn)而產(chǎn)生分布式事務(wù)捅僵。這兩種場景是大家比較容易忽略的。
分布式事務(wù)解決方案
知道了分布式事務(wù)產(chǎn)生的場景后眨层,接下來庙楚,我們就聊聊分布式事務(wù)具體有哪些解決方案。
2PC方案
2PC即兩階段提交協(xié)議趴樱,是將整個事務(wù)流程分為兩個階段馒闷,準(zhǔn)備階段(Prepare phase)、提交階段(commit phase)叁征,2是指兩個階段纳账,P是指準(zhǔn)備階段,C是指提交階段航揉。
這里塞祈,我們用MySQL數(shù)據(jù)庫舉例,MySQL數(shù)據(jù)庫支持兩階段提交協(xié)議帅涂,可以分為成功和失敗兩種情況议薪。
成功情況
失敗情況
具體流程如下:
準(zhǔn)備階段(Prepare phase): 事務(wù)管理器給每個參與者發(fā)送Prepare消息,每個數(shù)據(jù)庫參與者在本地執(zhí)行事 務(wù)媳友,并寫本地的Undo/Redo日志斯议,此時事務(wù)沒有提交。 (Undo日志是記錄修改前的數(shù)據(jù)醇锚,用于數(shù)據(jù)庫回滾哼御,Redo日志是記錄修改后的數(shù)據(jù),用于提交事務(wù)后寫入數(shù) 據(jù)文件)
提交階段(commit phase): 如果事務(wù)管理器收到了參與者的執(zhí)行失敗或者超時消息時焊唬,直接給每個參與者 發(fā)送回滾(Rollback)消息恋昼;否則,發(fā)送提交(Commit)消息赶促;參與者根據(jù)事務(wù)管理器的指令執(zhí)行提交或者回滾操 作液肌,并釋放事務(wù)處理過程中使用的鎖資源。
使用2PC方案時鸥滨,需要注意的是:必須在最后階段釋放鎖資源嗦哆。
可靠消息最終一致性方案
可靠消息最終一致性方案是指當(dāng)事務(wù)發(fā)起方執(zhí)行完成本地事務(wù)后并發(fā)出一條消息谤祖,事務(wù)參與方(消息消費者)一定能 夠接收消息并處理事務(wù)成功,此方案強(qiáng)調(diào)的是只要消息發(fā)給事務(wù)參與方最終事務(wù)要達(dá)到一致老速。
事務(wù)發(fā)起方(消息生產(chǎn)方)將消息發(fā)給消息中間件粥喜,事務(wù)參與方從消息中間件接收消息,事務(wù)發(fā)起方和消息中間件 之間橘券,事務(wù)參與方(消息消費方)和消息中間件之間都是通過網(wǎng)絡(luò)通信额湘,由于網(wǎng)絡(luò)通信的不確定性會導(dǎo)致分布式事 務(wù)問題。 所以约郁,我們在具體方案中會引入消息確認(rèn)服務(wù)和消息恢復(fù)服務(wù)缩挑。
使用可靠消息最終一致性方案時需要注意幾個問題:
本地事務(wù)與消息發(fā)送的原子性問題。
事務(wù)參與方接收消息的可靠性問題鬓梅。
消息重復(fù)消費的問題(需要實現(xiàn)冪等)供置。
TCC方案
TCC分為三個階段:
Try 階段 是做業(yè)務(wù)檢查(一致性)及資源預(yù)留(隔離),此階段僅是一個初步操作绽快,它和后續(xù)的Confirm 一起才能 真正構(gòu)成一個完整的業(yè)務(wù)邏輯芥丧。
Confirm 階段 是做確認(rèn)提交,Try階段所有分支事務(wù)執(zhí)行成功后開始執(zhí)行 Confirm坊罢。通常情況下续担,采用TCC則 認(rèn)為 Confirm階段是不會出錯的。即:只要Try成功活孩,Confirm一定成功物遇。若Confirm階段真的出錯了,需引 入重試機(jī)制或人工處理憾儒。
Cancel 階段 是在業(yè)務(wù)執(zhí)行錯誤需要回滾的狀態(tài)下執(zhí)行分支事務(wù)的業(yè)務(wù)取消询兴,預(yù)留資源釋放。通常情況下起趾,采 用TCC則認(rèn)為Cancel階段也是一定成功的诗舰。若Cancel階段真的出錯了,需引入重試機(jī)制或人工處理训裆。
使用TCC分布式解決方案時需要注意空回滾眶根、冪等、懸掛等問題边琉。
最大努力通知型方案
此種方案主要用于多個不同系統(tǒng)之前保證數(shù)據(jù)的最終一致性属百,大體如下圖所示。
使用最大努力通知型方案需要注意冪等和數(shù)據(jù)的回查操作变姨。
寫在最后
為了讓小伙伴們更好的了解本書诸老,在文章最后冰河附上幾張精美的圖片。
好了钳恕,今天就到這兒吧别伏,我是冰河,我們下期見~~