前言
我們都知道數(shù)據(jù)庫的事務(wù)滿足"ACID"特性铆农,A是指事務(wù)的原子性,C是指事務(wù)的一致性发钝,I指事務(wù)的隔離性顿涣,D指持久性。
最開始我們的數(shù)據(jù)量都很小酝豪,所有的數(shù)據(jù)都落在一個(gè)數(shù)據(jù)庫中。MySQL數(shù)據(jù)庫單表的最大數(shù)據(jù)量在百萬條左右精堕,隨著系統(tǒng)變大孵淘,數(shù)據(jù)越來越多,這個(gè)時(shí)候我們不得不將數(shù)據(jù)分布在不同的數(shù)據(jù)庫中存放歹篓,也就是常說的數(shù)據(jù)分片(sharding)瘫证。我們可以通過一定的分庫策略將同一個(gè)交易鏈路上的數(shù)據(jù)放到一個(gè)數(shù)據(jù)庫中,例如我們可以將一個(gè)訂單所有產(chǎn)生的數(shù)據(jù)放到一個(gè)數(shù)據(jù)庫中庄撮,按照訂單號(hào)來分庫背捌,這樣我們?cè)谏捎唵蜗嚓P(guān)數(shù)據(jù)的時(shí)候可以在單個(gè)數(shù)據(jù)庫上開啟事務(wù)來完成。
這樣看似完美的解決方案其實(shí)并不完美洞斯。例如我們想記錄用戶維度的訂單數(shù)據(jù)時(shí)毡庆,這個(gè)方案就無能為力了。于是分布式事務(wù)應(yīng)運(yùn)而生烙如。
分布式系統(tǒng)CAP BASE理論
說到分布式系統(tǒng)么抗,不得不說CAP和BASE理論,它是指導(dǎo)我們分布式系統(tǒng)的理論基礎(chǔ)亚铁。
CAP理論
加州大學(xué)伯克利分校Eric Brewer教授支持一個(gè)分布式系統(tǒng)無法滿足這樣三條特性:
- Consistency蝇刀,一致性:多個(gè)操作同時(shí)生效,不會(huì)出現(xiàn)部分生效的情況
- Availability徘溢,可用性:客戶端的每個(gè)請(qǐng)求在服務(wù)端能夠正確被響應(yīng)
- Partition tolerance吞琐,分區(qū)容錯(cuò)性:分區(qū)中部分節(jié)點(diǎn)掛了不會(huì)影響整體服務(wù)可用性,這也是分布式系統(tǒng)最基本的要求
分區(qū)容錯(cuò)性是一個(gè)分布式系統(tǒng)最基本的要求然爆,因此一般分布式系統(tǒng)都會(huì)滿足分區(qū)容錯(cuò)性站粟,否則就失去了分布式系統(tǒng)的意義。分布式系統(tǒng)一般會(huì)在一致性和可用性上做出取舍施蜜。例如犧牲一致性換區(qū)可用性卒蘸,這里說的犧牲一致性是指犧牲掉系統(tǒng)的"強(qiáng)一致性",最終我們的系統(tǒng)還是一致的,即所謂的"弱一致性"或者"最終一致性"缸沃。當(dāng)我們的系統(tǒng)需要保證強(qiáng)一致性時(shí)我們不得不犧牲掉可用性:當(dāng)系統(tǒng)部分節(jié)點(diǎn)延遲或者down機(jī)整個(gè)系統(tǒng)的服務(wù)將變得不可用恰起,也因此系統(tǒng)數(shù)據(jù)是強(qiáng)一致性的。
BASE理論
BASE理論是對(duì)CAP理論的進(jìn)一步擴(kuò)充:
- Basically Available(基本可用)
- Soft state(軟狀態(tài))
- Eventually consistent(最終一致性)
基本可用是指我們的系統(tǒng)無法做到百分百可用趾牧,但是可以保證例如"3個(gè)9"(99.9%)可用检盼。軟狀態(tài)是指允許我們的系統(tǒng)存在中間狀態(tài),在最終我們的系統(tǒng)可以達(dá)到最終一致性翘单。BASE理論強(qiáng)調(diào)的是系統(tǒng)的最終一致性吨枉。
分布式事務(wù)
目前分布式事務(wù)的解決方案主要有:二階段提交(2PC)、柔性補(bǔ)償事務(wù)(TCC)哄芜、本地消息表(異步確保)貌亭、MQ事務(wù)消息等。
二階段提交
二階段事務(wù)分為兩個(gè)階段:階段一事務(wù)協(xié)調(diào)者通知各個(gè)節(jié)點(diǎn)執(zhí)行事務(wù)預(yù)提交认臊;階段二協(xié)調(diào)者根據(jù)各個(gè)節(jié)點(diǎn)的響應(yīng)來通知各個(gè)節(jié)點(diǎn)提交或者回滾事務(wù)操作圃庭。
兩階段提交這種解決方案屬于犧牲了一部分可用性來換取的一致性。
優(yōu)點(diǎn): 盡量保證了數(shù)據(jù)的強(qiáng)一致失晴,適合對(duì)數(shù)據(jù)強(qiáng)一致要求很高的關(guān)鍵領(lǐng)域剧腻。(其實(shí)也不能100%保證強(qiáng)一致)
缺點(diǎn): 實(shí)現(xiàn)復(fù)雜,犧牲了可用性涂屁,同步阻塞對(duì)性能影響較大书在,不適合高并發(fā)高性能場(chǎng)景。
補(bǔ)償事務(wù)(TCC)
TCC 其實(shí)就是采用的補(bǔ)償機(jī)制拆又,其核心思想是:針對(duì)每個(gè)操作儒旬,都要注冊(cè)一個(gè)與其對(duì)應(yīng)的確認(rèn)和補(bǔ)償(撤銷)操作。它分為三個(gè)階段:
Try 階段主要是對(duì)業(yè)務(wù)系統(tǒng)做檢測(cè)及資源預(yù)留
Confirm 階段主要是對(duì)業(yè)務(wù)系統(tǒng)做確認(rèn)提交遏乔,Try階段執(zhí)行成功并開始執(zhí)行 Confirm階段時(shí)义矛,默認(rèn) Confirm階段是不會(huì)出錯(cuò)的。即:只要Try成功盟萨,Confirm一定成功凉翻。
Cancel 階段主要是在業(yè)務(wù)執(zhí)行錯(cuò)誤,需要回滾的狀態(tài)下執(zhí)行的業(yè)務(wù)取消捻激,預(yù)留資源釋放制轰。
舉個(gè)例子,假入 Bob 要向 Smith 轉(zhuǎn)賬胞谭,思路大概是:
我們有一個(gè)本地方法垃杖,里面依次調(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é)接口對(duì)應(yīng)的解凍方法 (Cancel)骇钦。
優(yōu)點(diǎn): 跟2PC比起來宛渐,實(shí)現(xiàn)以及流程相對(duì)簡(jiǎn)單了一些,但數(shù)據(jù)的一致性比2PC也要差一些
缺點(diǎn): 缺點(diǎn)還是比較明顯的眯搭,在2,3步中都有可能失敗窥翩。TCC屬于應(yīng)用層的一種補(bǔ)償方式,所以需要程序員在實(shí)現(xiàn)的時(shí)候多寫很多補(bǔ)償?shù)拇a鳞仙,在一些場(chǎng)景中寇蚊,一些業(yè)務(wù)流程可能用TCC不太好定義及處理。
本地消息表(異步確保)
本地消息表這種實(shí)現(xiàn)方式應(yīng)該是業(yè)界使用最多的繁扎,其核心思想是將分布式事務(wù)拆分成本地事務(wù)進(jìn)行處理幔荒,這種思路是來源于ebay。我們可以從下面的流程圖中看出其中的一些細(xì)節(jié):
基本思路就是:
消息生產(chǎn)方梳玫,需要額外建一個(gè)消息表,并記錄消息發(fā)送狀態(tài)右犹。消息表和業(yè)務(wù)數(shù)據(jù)要在一個(gè)事務(wù)里提交提澎,也就是說他們要在一個(gè)數(shù)據(jù)庫里面。然后消息會(huì)經(jīng)過MQ發(fā)送到消息的消費(fèi)方念链。如果消息發(fā)送失敗盼忌,會(huì)進(jìn)行重試發(fā)送。
消息消費(fèi)方掂墓,需要處理這個(gè)消息谦纱,并完成自己的業(yè)務(wù)邏輯。此時(shí)如果本地事務(wù)處理成功君编,表明已經(jīng)處理成功了跨嘉,如果處理失敗吃嘿,那么就會(huì)重試執(zhí)行祠乃。如果是業(yè)務(wù)上面的失敗,可以給生產(chǎn)方發(fā)送一個(gè)業(yè)務(wù)補(bǔ)償消息兑燥,通知生產(chǎn)方進(jìn)行回滾等操作黄橘。
生產(chǎn)方和消費(fèi)方定時(shí)掃描本地消息表蓖康,把還沒處理完成的消息或者失敗的消息再發(fā)送一遍。如果有靠譜的自動(dòng)對(duì)賬補(bǔ)賬邏輯,這種方案還是非常實(shí)用的怠益。
這種方案遵循BASE理論驱富,采用的是最終一致性,筆者認(rèn)為是這幾種方案里面比較適合實(shí)際業(yè)務(wù)場(chǎng)景的,即不會(huì)出現(xiàn)像2PC那樣復(fù)雜的實(shí)現(xiàn)(當(dāng)調(diào)用鏈很長的時(shí)候沛膳,2PC的可用性是非常低的),也不會(huì)像TCC那樣可能出現(xiàn)確認(rèn)或者回滾不了的情況馍盟。
優(yōu)點(diǎn): 一種非常經(jīng)典的實(shí)現(xiàn)于置,避免了分布式事務(wù),實(shí)現(xiàn)了最終一致性贞岭。
缺點(diǎn): 消息表會(huì)耦合到業(yè)務(wù)系統(tǒng)中八毯,如果沒有封裝好的解決方案,會(huì)有很多雜活需要處理瞄桨。
MQ事務(wù)消息
有一些第三方的MQ是支持事務(wù)消息的话速,比如RocketMQ,他們支持事務(wù)消息的方式也是類似于采用的二階段提交芯侥,但是市面上一些主流的MQ都是不支持事務(wù)消息的泊交,比如 RabbitMQ 和 Kafka 都不支持。
以阿里的 RocketMQ 中間件為例柱查,其思路大致為:
第一階段Prepared消息廓俭,會(huì)拿到消息的地址。
第二階段執(zhí)行本地事務(wù)唉工,第三階段通過第一階段拿到的地址去訪問消息研乒,并修改狀態(tài)。
也就是說在業(yè)務(wù)方法內(nèi)要想消息隊(duì)列提交兩次請(qǐng)求淋硝,一次發(fā)送消息和一次確認(rèn)消息雹熬。如果確認(rèn)消息發(fā)送失敗了RocketMQ會(huì)定期掃描消息集群中的事務(wù)消息,這時(shí)候發(fā)現(xiàn)了Prepared消息谣膳,它會(huì)向消息發(fā)送者確認(rèn)竿报,所以生產(chǎn)方需要實(shí)現(xiàn)一個(gè)check接口,RocketMQ會(huì)根據(jù)發(fā)送端設(shè)置的策略來決定是回滾還是繼續(xù)發(fā)送確認(rèn)消息继谚。這樣就保證了消息發(fā)送與本地事務(wù)同時(shí)成功或同時(shí)失敗烈菌。
優(yōu)點(diǎn): 實(shí)現(xiàn)了最終一致性,不需要依賴本地?cái)?shù)據(jù)庫事務(wù)犬庇。
缺點(diǎn): 實(shí)現(xiàn)難度大僧界,主流MQ不支持。
總結(jié)
- 目前分布式事務(wù)主要有:二階段提交臭挽、TCC柔性事務(wù)捂襟、本地消息表(異步確保)和MQ事務(wù)消息等解決方案。二階段提交可以確保數(shù)據(jù)的強(qiáng)一致性欢峰,其他的解決方案屬于最終一致性解決方案葬荷。
- 分布式事務(wù)雖然能保證數(shù)據(jù)的強(qiáng)一致性涨共,但是性能低。使用前需要確定是否真的需要分布式事務(wù)宠漩。