分布式事務(wù)指事務(wù)的操作位于不同的節(jié)點上,需要保證事務(wù)的 AICD 特性。 例如在下單場景下,庫存和訂單如果不在同一個節(jié)點上,就涉及分布式事務(wù)。 在分布式系統(tǒng)中,要實現(xiàn)分布式事務(wù),無外乎那幾種解決方案。 兩階段提交(Two-phase Commit,2PC),通過引入?yún)f(xié)調(diào)者(Coordinator)來協(xié)調(diào)參與者的行為,并最終決定這些參與者是否要真正執(zhí)行事務(wù)。1. 運行過程1.1 準(zhǔn)備階段協(xié)調(diào)者詢問參與者事務(wù)是否執(zhí)行成功,參與者發(fā)回事務(wù)執(zhí)行結(jié)果。 1.2 提交階段 如果事務(wù)在每個參與者上都執(zhí)行成功,事務(wù)協(xié)調(diào)者發(fā)送通知讓參與者提交事務(wù);否則,協(xié)調(diào)者發(fā)送通知讓參與者回滾事務(wù)。需要注意的是,在準(zhǔn)備階段,參與者執(zhí)行了事務(wù),但是還未提交。只有在提交階段接收到協(xié)調(diào)者發(fā)來的通知后,才進(jìn)行提交或者回滾。 2. 存在的問題
2.3 數(shù)據(jù)不一致 在階段二,如果協(xié)調(diào)者只發(fā)送了部分 Commit 消息,此時網(wǎng)絡(luò)發(fā)生異常,那么只有部分參與者接收到 Commit 消息,也就是說只有部分參與者提交了事務(wù),使得系統(tǒng)數(shù)據(jù)不一致。 TCC 其實就是采用的補償機(jī)制,其核心思想是:針對每個操作,都要注冊一個與其對應(yīng)的確認(rèn)和補償(撤銷)操作。它分為三個階段:舉個例子,假入 Bob 要向 Smith 轉(zhuǎn)賬,思路大概是:我們有一個本地方法,里面依次調(diào)用- 首先在 Try 階段,要先調(diào)用遠(yuǎn)程接口把 Smith 和 Bob 的錢給凍結(jié)起來。
- 在 Confirm 階段,執(zhí)行遠(yuǎn)程調(diào)用的轉(zhuǎn)賬的操作,轉(zhuǎn)賬成功進(jìn)行解凍。
- 如果第2步執(zhí)行成功,那么轉(zhuǎn)賬成功,如果第二步執(zhí)行失敗,則調(diào)用遠(yuǎn)程凍結(jié)接口對應(yīng)的解凍方法 (Cancel)。
優(yōu)點: 跟2PC比起來,實現(xiàn)以及流程相對簡單了一些,但數(shù)據(jù)的一致性比2PC也要差一些缺點: 缺點還是比較明顯的,在2,3步中都有可能失敗。TCC屬于應(yīng)用層的一種補償方式,所以需要程序員在實現(xiàn)的時候多寫很多補償?shù)拇a,在一些場景中,一些業(yè)務(wù)流程可能用TCC不太好定義及處理。本地消息表與業(yè)務(wù)數(shù)據(jù)表處于同一個數(shù)據(jù)庫中,這樣就能利用本地事務(wù)來保證在對這兩個表的操作滿足事務(wù)特性,并且使用了消息隊列來保證最終一致性。- 在分布式事務(wù)操作的一方完成寫業(yè)務(wù)數(shù)據(jù)的操作之后向本地消息表發(fā)送一個消息,本地事務(wù)能保證這個消息一定會被寫入本地消息表中。
- 之后將本地消息表中的消息轉(zhuǎn)發(fā)到 Kafka 等消息隊列中,如果轉(zhuǎn)發(fā)成功則將消息從本地消息表中刪除,否則繼續(xù)重新轉(zhuǎn)發(fā)。
- 在分布式事務(wù)操作的另一方從消息隊列中讀取一個消息,并執(zhí)行消息中的操作。

優(yōu)點: 一種非常經(jīng)典的實現(xiàn),避免了分布式事務(wù),實現(xiàn)了最終一致性。 缺點: 消息表會耦合到業(yè)務(wù)系統(tǒng)中,如果沒有封裝好的解決方案,會有很多雜活需要處理。
優(yōu)點: 實現(xiàn)了最終一致性,不需要依賴本地數(shù)據(jù)庫事務(wù)。 缺點: 實現(xiàn)難度大,主流MQ不支持,RocketMQ事務(wù)消息部分代碼也未開源。通過本文我們總結(jié)并對比了幾種分布式分解方案的優(yōu)缺點,分布式事務(wù)本身是一個技術(shù)難題,是沒有一種完美的方案應(yīng)對所有場景的,具體還是要根據(jù)業(yè)務(wù)場景去抉擇吧。筆者上家公司是試用阿里RocketMQ去實現(xiàn)的分布式事務(wù),現(xiàn)在也有除了很多分布式事務(wù)的協(xié)調(diào)器,比如LCN等,大家可以多去嘗試。
|