新聞資訊
NEWS解決分布式系統(tǒng)的一致性問(wèn)題,我們需要了解哪些理論?
一致性是一個(gè)抽象的、具有多重含義的計(jì)算機(jī)術(shù)語(yǔ),在不同應(yīng)用場(chǎng)景下,有不同的定義和含義。在傳統(tǒng)的IT時(shí)代,一致性通常指強(qiáng)一致性,強(qiáng)一致性通常體現(xiàn)在你中有我、我中有你、渾然一體;而在互聯(lián)網(wǎng)時(shí)代,一致性的含義遠(yuǎn)遠(yuǎn)超出了它原有的含義,在我們討論互聯(lián)網(wǎng)時(shí)代的一致性之前,我們先了解一下互聯(lián)網(wǎng)時(shí)代的特點(diǎn),互聯(lián)網(wǎng)時(shí)代信息量巨大、需要計(jì)算能力巨大,不但對(duì)用戶(hù)響應(yīng)速度要求快,而且吞吐量指標(biāo)也要向外擴(kuò)展(既:水平伸縮)。
于是單節(jié)點(diǎn)的服務(wù)器無(wú)法滿(mǎn)足需求,服務(wù)節(jié)點(diǎn)開(kāi)始池化,想想那個(gè)經(jīng)典的故事,一只筷子一折就斷,一把筷子怎么都折不斷,可見(jiàn)人多力量大的思想是多么的重要,但是人多也不一定能解決所有事情,還得進(jìn)行有序、合理的分配任務(wù),進(jìn)行有效的管理,于是互聯(lián)網(wǎng)時(shí)代談?wù)撟疃嗟脑掝}就是拆分,拆分一般分為“水平拆分”和“垂直拆分”(大家不要對(duì)應(yīng)到數(shù)據(jù)庫(kù)或者緩存拆分,這里主要表達(dá)一種邏輯)。
這里,“水平拆分”指的是同一個(gè)功能由于單機(jī)節(jié)點(diǎn)無(wú)法滿(mǎn)足性能需求,需要擴(kuò)展成為多節(jié)點(diǎn),多個(gè)節(jié)點(diǎn)具有一致的功能,組成一個(gè)服務(wù)池,一個(gè)節(jié)點(diǎn)服務(wù)一部分的請(qǐng)求量,團(tuán)結(jié)起來(lái)共同處理大規(guī)模高并發(fā)的請(qǐng)求量?!按怪辈鸱帧敝傅氖前凑展δ懿鸱?,秉著“專(zhuān)業(yè)的人干專(zhuān)業(yè)的事兒”的原則,把一個(gè)復(fù)雜的功能拆分到多個(gè)單一的簡(jiǎn)單的元功能,不同的元功能組合在一起,和未拆分前完成的功能是一致的,由于每個(gè)元功能職責(zé)單一、功能簡(jiǎn)單,讓維護(hù)和變更都變得更簡(jiǎn)單、安全,更易于產(chǎn)品版本的迭代,在這樣的一個(gè)互聯(lián)網(wǎng)的時(shí)代和環(huán)境,一致性指分布式服務(wù)化系統(tǒng)之間的弱一致性,包括應(yīng)用系統(tǒng)一致性和數(shù)據(jù)一致性。
無(wú)論是水平拆分還是垂直拆分,都解決了特定場(chǎng)景下的特定問(wèn)題,凡事有好的一面,都會(huì)有壞的一面,拆分后的系統(tǒng)或者服務(wù)化的系統(tǒng)最大的問(wèn)題就是一致性問(wèn)題,這么多個(gè)具有元功能的模塊,或者同一個(gè)功能池中的多個(gè)節(jié)點(diǎn)之間,如何保證他們的信息是一致的、工作步伐是一致的、狀態(tài)是一致的、互相協(xié)調(diào)有序的工作呢?
本文根據(jù)作者在互聯(lián)網(wǎng)企業(yè)的實(shí)際項(xiàng)目經(jīng)驗(yàn),對(duì)服務(wù)化系統(tǒng)中最難解決的一致性問(wèn)題進(jìn)行研究和探討,試圖從實(shí)踐經(jīng)驗(yàn)中找到規(guī)律,抽象出模式,分享給大家,希望對(duì)大家的項(xiàng)目實(shí)施有所幫助,在對(duì)實(shí)踐的總結(jié)中也會(huì)對(duì)相關(guān)的一致性術(shù)語(yǔ)做最樸實(shí)的解釋?zhuān)M軒椭蠹覐氐桌斫庖恢滦缘谋举|(zhì),并能將其應(yīng)用到實(shí)踐,解決讀者現(xiàn)實(shí)中遇到的服務(wù)化系統(tǒng)的一致性問(wèn)題。
我們遇到的問(wèn)題
本節(jié)列舉不一致會(huì)導(dǎo)致的種種問(wèn)題,這也包括一例生活中的問(wèn)題。
案例1:買(mǎi)房
假如你想要享受生活的隨意,只想買(mǎi)個(gè)兩居,不想讓房貸有太大壓力,而你媳婦卻想要買(mǎi)個(gè)三居,還得帶花園的,那么你們就不一致了,不一致導(dǎo)致生活不愉快、不協(xié)調(diào),嚴(yán)重情況下還會(huì)吵架,可見(jiàn)生活中的不一致問(wèn)題影響很大。
案例2:轉(zhuǎn)賬
轉(zhuǎn)賬是經(jīng)典的不一致案例,設(shè)想一下銀行為你處理一筆轉(zhuǎn)賬,扣減你賬戶(hù)上的余額,然后增加別人賬戶(hù)的余額;如果扣減你的賬戶(hù)余額成功,增加別人賬戶(hù)余額失敗,那么你就會(huì)損失這筆資金。反過(guò)來(lái),如果扣減你的賬戶(hù)余額失敗,增加別人賬戶(hù)余額成功,那么銀行就會(huì)損失這筆資金,銀行需要賠付。對(duì)于資金處理系統(tǒng)來(lái)說(shuō),上面任何一種場(chǎng)景都是不允許發(fā)生的,一旦發(fā)生就會(huì)有資金損失,后果是不堪設(shè)想的,嚴(yán)重情況會(huì)讓一個(gè)公司瞬間倒閉,可參考案例。
案例3:下訂單和扣庫(kù)存
電商系統(tǒng)中也有一個(gè)經(jīng)典的案例,下訂單和扣庫(kù)存如何保持一致,如果先下訂單,扣庫(kù)存失敗,那么將會(huì)導(dǎo)致超賣(mài);如果下訂單沒(méi)有成功,扣庫(kù)存成功,那么會(huì)導(dǎo)致少賣(mài)。兩種情況都會(huì)導(dǎo)致運(yùn)營(yíng)成本的增加,嚴(yán)重情況下需要賠付。
案例4:同步超時(shí)
服務(wù)化的系統(tǒng)間調(diào)用常常因?yàn)榫W(wǎng)絡(luò)問(wèn)題導(dǎo)致系統(tǒng)間調(diào)用超時(shí),即使是網(wǎng)絡(luò)很好的機(jī)房,在億次流量的基數(shù)下,同步調(diào)用超時(shí)也是家常便飯。系統(tǒng)A同步調(diào)用系統(tǒng)B超時(shí),系統(tǒng)A可以明確得到超時(shí)反饋,但是無(wú)法確定系統(tǒng)B是否已經(jīng)完成了預(yù)定的功能或者沒(méi)有完成預(yù)定的功能。于是,系統(tǒng)A就迷茫了,不知道應(yīng)該繼續(xù)做什么,如何反饋給使用方。(曾經(jīng)的一個(gè)B2B產(chǎn)品的客戶(hù)要求接口超時(shí)重新通知他們,這個(gè)在技術(shù)上是難以實(shí)現(xiàn)的,因?yàn)榉?wù)器本身可能并不知道自己超時(shí),可能會(huì)繼續(xù)正常的返回?cái)?shù)據(jù),只是客戶(hù)端并沒(méi)有接受到結(jié)果罷了,因此這不是一個(gè)合理的解決方案)。
案例5:異步回調(diào)超時(shí)
此案例和上一個(gè)同步超時(shí)案例類(lèi)似,不過(guò)這個(gè)場(chǎng)景使用了異步回調(diào),系統(tǒng)A同步調(diào)用系統(tǒng)B發(fā)起指令,系統(tǒng)B采用受理模式,受理后則返回受理成功,然后系統(tǒng)B異步通知系統(tǒng)A。在這個(gè)過(guò)程中,如果系統(tǒng)A由于某種原因遲遲沒(méi)有收到回調(diào)結(jié)果,那么兩個(gè)系統(tǒng)間的狀態(tài)就不一致,互相認(rèn)知不同會(huì)導(dǎo)致系統(tǒng)間發(fā)生錯(cuò)誤,嚴(yán)重情況下會(huì)影響核心事務(wù),甚至?xí)?dǎo)致資金損失。
案例6:掉單
分布式系統(tǒng)中,兩個(gè)系統(tǒng)協(xié)作處理一個(gè)流程,分別為對(duì)方的上下游,如果一個(gè)系統(tǒng)中存在一個(gè)請(qǐng)求,通常指訂單,另外一個(gè)系統(tǒng)不存在,則導(dǎo)致掉單,掉單的后果很?chē)?yán)重,有時(shí)候也會(huì)導(dǎo)致資金損失。
案例7:系統(tǒng)間狀態(tài)不一致
這個(gè)案例與上面掉單案例類(lèi)似,不同的是兩個(gè)系統(tǒng)間都存在請(qǐng)求,但是請(qǐng)求的狀態(tài)不一致。
案例8:緩存和數(shù)據(jù)庫(kù)不一致
交易相關(guān)系統(tǒng)基本離不開(kāi)關(guān)系型數(shù)據(jù)庫(kù),依賴(lài)關(guān)系型數(shù)據(jù)庫(kù)提供的ACID特性(后面介紹),但是在大規(guī)模高并發(fā)的互聯(lián)網(wǎng)系統(tǒng)里,一些特殊的場(chǎng)景對(duì)讀的性能要求極高,服務(wù)于交易的數(shù)據(jù)庫(kù)難以抗住大規(guī)模的讀流量,通常需要在數(shù)據(jù)庫(kù)前墊緩存,那么緩存和數(shù)據(jù)庫(kù)之間的數(shù)據(jù)如何保持一致性?是要保持強(qiáng)一致呢還是弱一致性呢?
案例9:本地緩存節(jié)點(diǎn)間不一致
一個(gè)服務(wù)池上的多個(gè)節(jié)點(diǎn)為了滿(mǎn)足較高的性能需求,需要使用本地緩存,使用了本地緩存,每個(gè)節(jié)點(diǎn)都會(huì)有一份緩存數(shù)據(jù)的拷貝,如果這些數(shù)據(jù)是靜態(tài)的、不變的,那永遠(yuǎn)都不會(huì)有問(wèn)題,但是如果這些數(shù)據(jù)是半靜態(tài)的或者常被更新的,當(dāng)被更新的時(shí)候,各個(gè)節(jié)點(diǎn)更新是有先后順序的,在更新的瞬間,各個(gè)節(jié)點(diǎn)的數(shù)據(jù)是不一致的,如果這些數(shù)據(jù)是為某一個(gè)開(kāi)關(guān)服務(wù)的,想象一下重復(fù)的請(qǐng)求走進(jìn)了不同的節(jié)點(diǎn)(在failover或者補(bǔ)償導(dǎo)致的場(chǎng)景下,重復(fù)請(qǐng)求是一定會(huì)發(fā)生的,也是服務(wù)化系統(tǒng)必須處理的),一個(gè)請(qǐng)求走了開(kāi)關(guān)打開(kāi)的邏輯,同時(shí)另外一個(gè)請(qǐng)求走了開(kāi)關(guān)關(guān)閉的邏輯,這導(dǎo)致請(qǐng)求被處理兩次,最壞的情況下會(huì)導(dǎo)致災(zāi)難性的后果,就是資金損失。
案例10:緩存數(shù)據(jù)結(jié)構(gòu)不一致
這個(gè)案例會(huì)時(shí)有發(fā)生,某系統(tǒng)需要種某一數(shù)據(jù)結(jié)構(gòu)的緩存,這一數(shù)據(jù)結(jié)構(gòu)有多個(gè)數(shù)據(jù)元素組成,其中,某個(gè)數(shù)據(jù)元素都需要從數(shù)據(jù)庫(kù)中或者服務(wù)中獲取,如果一部分?jǐn)?shù)據(jù)元素獲取失敗,由于程序處理不正確,仍然將不完全的數(shù)據(jù)結(jié)構(gòu)存入緩存,那么緩存的消費(fèi)者消費(fèi)的時(shí)候很有可能因?yàn)闆](méi)有合理處理異常情況而出錯(cuò)。
模式以及思路生活中不一致問(wèn)題的解決
大家回顧一下上一節(jié)列舉的生活中的案例1-買(mǎi)房,如果置身事外來(lái)看,解決這種不一致的辦法有兩個(gè),一個(gè)是避免不一致的發(fā)生,如果已經(jīng)是媳婦了就不好辦了:),還有一種方法就是慢慢的補(bǔ)償,先買(mǎi)個(gè)兩居,然后慢慢的等資金充裕了再換三居,買(mǎi)比特幣賺了再換帶花園的房子,于是問(wèn)題最終被解決了,最終大家處于一致的狀態(tài),都開(kāi)心了。這樣可以解決案例1的問(wèn)題,很自然由于有了過(guò)渡的方法,問(wèn)題在不經(jīng)意間就消失了,可見(jiàn)“過(guò)渡”也是解決一致性問(wèn)題的一個(gè)模式。
從案例1的解決方案來(lái)看,我們要解決一致性問(wèn)題,一個(gè)最直接最簡(jiǎn)單的方法就是保持強(qiáng)一致性,對(duì)于案例1的情況,盡量避免在結(jié)婚前兩個(gè)人能夠互相了解達(dá)成一致,避免不一致問(wèn)題的發(fā)生;不過(guò)有些事情事已至此,發(fā)生了就是發(fā)生了,出現(xiàn)了不一致的問(wèn)題,我們應(yīng)該考慮去補(bǔ)償,盡最大的努力從不一致?tīng)顟B(tài)修復(fù)到一致?tīng)顟B(tài),避免損失全部或者一部分,也不失為一個(gè)好方法。
因此,避免不一致是上策,出現(xiàn)了不一致及時(shí)發(fā)現(xiàn)及時(shí)修復(fù)是中策,有問(wèn)題不積極解決留給他人解決是下策。
酸堿平衡理論
ACID在英文中的意思是“酸”,BASE的意識(shí)是“堿”,這一段講的是“酸堿平衡”的故事。
1. ACID(酸)
如何保證強(qiáng)一致性呢?計(jì)算機(jī)專(zhuān)業(yè)的童鞋在學(xué)習(xí)關(guān)系型數(shù)據(jù)庫(kù)的時(shí)候都學(xué)習(xí)了ACID原理,這里對(duì)ACID做個(gè)簡(jiǎn)單的介紹。如果想全面的學(xué)習(xí)ACID原理,請(qǐng)參考ACID。
關(guān)系型數(shù)據(jù)庫(kù)天生就是解決具有復(fù)雜事務(wù)場(chǎng)景的問(wèn)題,關(guān)系型數(shù)據(jù)庫(kù)完全滿(mǎn)足ACID的特性。
ACID指的是:
A: Atomicity,原子性
C: Consistency,一致性
I: Isolation,隔離性
D: Durability,持久性
具有ACID的特性的數(shù)據(jù)庫(kù)支持強(qiáng)一致性,強(qiáng)一致性代表數(shù)據(jù)庫(kù)本身不會(huì)出現(xiàn)不一致,每個(gè)事務(wù)是原子的,或者成功或者失敗,事物間是隔離的,互相完全不影響,而且最終狀態(tài)是持久落盤(pán)的,因此,數(shù)據(jù)庫(kù)會(huì)從一個(gè)明確的狀態(tài)到另外一個(gè)明確的狀態(tài),中間的臨時(shí)狀態(tài)是不會(huì)出現(xiàn)的,如果出現(xiàn)也會(huì)及時(shí)的自動(dòng)的修復(fù),因此是強(qiáng)一致的。
3個(gè)典型的關(guān)系型數(shù)據(jù)庫(kù)Oracle、Mysql、Db2都能保證強(qiáng)一致性,Oracle和Mysql使用多版本控制協(xié)議實(shí)現(xiàn),而DB2使用改進(jìn)的兩階段提交協(xié)議來(lái)實(shí)現(xiàn)。
如果你在為交易相關(guān)系統(tǒng)做技術(shù)選型,交易的存儲(chǔ)應(yīng)該只考慮關(guān)系型數(shù)據(jù)庫(kù),對(duì)于核心系統(tǒng),如果需要較好的性能,可以考慮使用更強(qiáng)悍的硬件,這種向上擴(kuò)展(升級(jí)硬件)雖然成本較高,但是是最簡(jiǎn)單粗暴有效的方式,另外,Nosql完全不適合交易場(chǎng)景,Nosql主要用來(lái)做數(shù)據(jù)分析、ETL、報(bào)表、數(shù)據(jù)挖掘、推薦、日志處理等非交易場(chǎng)景。
前面提到的案例2-轉(zhuǎn)賬和案例3-下訂單和扣庫(kù)存都可以利用關(guān)系型數(shù)據(jù)庫(kù)的強(qiáng)一致性解決。
然而,前面提到,互聯(lián)網(wǎng)項(xiàng)目多數(shù)具有大規(guī)模高并發(fā)的特性,必須應(yīng)用拆分的理念,對(duì)高并發(fā)的壓力采取“大而化小、小而化了”的方法,否則難以滿(mǎn)足動(dòng)輒億級(jí)流量的需求,即使使用關(guān)系型數(shù)據(jù)庫(kù),單機(jī)也難以滿(mǎn)足存儲(chǔ)和TPS上的需求。為了保證案例2-轉(zhuǎn)賬可以利用關(guān)系型數(shù)據(jù)庫(kù)的強(qiáng)一致性,在拆分的時(shí)候盡量的把轉(zhuǎn)賬相關(guān)的賬戶(hù)放入一個(gè)數(shù)據(jù)庫(kù)分片,對(duì)于案例3,盡量的保證把訂單和庫(kù)存放入同一個(gè)數(shù)據(jù)庫(kù)分片,這樣通過(guò)關(guān)系型數(shù)據(jù)庫(kù)自然就解決了不一致的問(wèn)題。
然而,有些時(shí)候事與愿違,由于業(yè)務(wù)規(guī)則的限制,無(wú)法將相關(guān)的數(shù)據(jù)分到同一個(gè)數(shù)據(jù)庫(kù)分片,這個(gè)時(shí)候我們就需要實(shí)現(xiàn)最終一致性。
對(duì)于案例2-轉(zhuǎn)賬場(chǎng)景,假設(shè)賬戶(hù)數(shù)量巨大,對(duì)賬戶(hù)存儲(chǔ)進(jìn)行了拆分,關(guān)系型數(shù)據(jù)庫(kù)一共分了8個(gè)實(shí)例,每個(gè)實(shí)例8個(gè)庫(kù),每個(gè)庫(kù)8個(gè)表,共512張表,假如要轉(zhuǎn)賬的兩個(gè)賬戶(hù)正好落在了一個(gè)庫(kù)里,那么可以依賴(lài)關(guān)系型數(shù)據(jù)庫(kù)的事務(wù)保持強(qiáng)一致性。
如果要轉(zhuǎn)賬的兩個(gè)賬戶(hù)正好落在了不同的庫(kù)里,轉(zhuǎn)賬操作是無(wú)法封裝在同一個(gè)數(shù)據(jù)庫(kù)事務(wù)中的,這個(gè)時(shí)候會(huì)發(fā)生一個(gè)庫(kù)的賬戶(hù)扣減余額成功,另外一個(gè)庫(kù)的賬戶(hù)增加余額失敗的情況。
對(duì)于這種情況,我們需要繼續(xù)探討解決之道,CAP原理和BASE原理,BASE原理通過(guò)記錄事務(wù)的中間的臨時(shí)狀態(tài),實(shí)現(xiàn)最終一致性。
2. CAP(帽子理論)
由于對(duì)系統(tǒng)或者數(shù)據(jù)進(jìn)行了拆分,我們的系統(tǒng)不再是單機(jī)系統(tǒng),而是分布式系統(tǒng),針對(duì)分布式系的帽子理論包含三個(gè)元素:
C:Consistency,一致性, 數(shù)據(jù)一致更新,所有數(shù)據(jù)變動(dòng)都是同步的
A:Availability,可用性, 好的響應(yīng)性能,完全的可用性指的是在任何故障模型下,服務(wù)都會(huì)在有限的時(shí)間處理響應(yīng)
P:Partition tolerance,分區(qū)容錯(cuò)性,可靠性
帽子理論證明,任何分布式系統(tǒng)只可同時(shí)滿(mǎn)足二點(diǎn),沒(méi)法三者兼顧。關(guān)系型數(shù)據(jù)庫(kù)由于關(guān)系型數(shù)據(jù)庫(kù)是單節(jié)點(diǎn)的,因此,不具有分區(qū)容錯(cuò)性,但是具有一致性和可用性,而分布式的服務(wù)化系統(tǒng)都需要滿(mǎn)足分區(qū)容錯(cuò)性,那么我們必須在一致性和可用性中進(jìn)行權(quán)衡,具體表現(xiàn)在服務(wù)化系統(tǒng)處理的異常請(qǐng)求在某一個(gè)時(shí)間段內(nèi)可能是不完全的,但是經(jīng)過(guò)自動(dòng)的或者手工的補(bǔ)償后,達(dá)到了最終的一致性。
BASE(堿)
BASE理論解決CAP理論提出了分布式系統(tǒng)的一致性和可用性不能兼得的問(wèn)題,BASE在英文中有“堿”的意思,對(duì)應(yīng)本節(jié)開(kāi)頭的ACID在英文中“酸”的意思,基于這兩個(gè)名詞提出了酸堿平衡的結(jié)論,簡(jiǎn)單來(lái)說(shuō)是在不同的場(chǎng)景下,可以分別利用ACID和BASE來(lái)解決分布式服務(wù)化系統(tǒng)的一致性問(wèn)題。
BASE模型與ACID模型截然不同,滿(mǎn)足CAP理論,通過(guò)犧牲強(qiáng)一致性,獲得可用性,一般應(yīng)用在服務(wù)化系統(tǒng)的應(yīng)用層或者大數(shù)據(jù)處理系統(tǒng),通過(guò)達(dá)到最終一致性來(lái)盡量滿(mǎn)足業(yè)務(wù)的絕大部分需求。
BASE模型包含個(gè)三個(gè)元素:
BA:Basically Available,基本可用
S:Soft State,軟狀態(tài),狀態(tài)可以有一段時(shí)間不同步
E:Eventually Consistent,最終一致,最終數(shù)據(jù)是一致的就可以了,而不是時(shí)時(shí)保持強(qiáng)一致
BASE模型的軟狀態(tài)是實(shí)現(xiàn)BASE理論的方法,基本可用和最終一致是目標(biāo)。按照BASE模型實(shí)現(xiàn)的系統(tǒng),由于不保證強(qiáng)一致性,系統(tǒng)在處理請(qǐng)求的過(guò)程中,可以存在短暫的不一致,在短暫的不一致窗口請(qǐng)求處理處在臨時(shí)狀態(tài)中,系統(tǒng)在做每步操作的時(shí)候,通過(guò)記錄每一個(gè)臨時(shí)狀態(tài),在系統(tǒng)出現(xiàn)故障的時(shí)候,可以從這些中間狀態(tài)繼續(xù)未完成的請(qǐng)求處理或者退回到原始狀態(tài),最后達(dá)到一致的狀態(tài)。
以案例1-轉(zhuǎn)賬為例,我們把用戶(hù)A給用戶(hù)B轉(zhuǎn)賬分成四個(gè)階段,第一個(gè)階段用戶(hù)A準(zhǔn)備轉(zhuǎn)賬,第二個(gè)階段從用戶(hù)A賬戶(hù)扣減余額,第三個(gè)階段對(duì)用戶(hù)B增加余額,第四個(gè)階段完成轉(zhuǎn)賬。系統(tǒng)需要記錄操作過(guò)程中每一步驟的狀態(tài),一旦系統(tǒng)出現(xiàn)故障,系統(tǒng)能夠自動(dòng)發(fā)現(xiàn)沒(méi)有完成的任務(wù),然后,根據(jù)任務(wù)所處的狀態(tài),繼續(xù)執(zhí)行任務(wù),最終完成任務(wù),達(dá)到一致的最終狀態(tài)。
在實(shí)際應(yīng)用中,上面這個(gè)過(guò)程通常是通過(guò)持久化執(zhí)行任務(wù)的狀態(tài)和環(huán)境信息,一旦出現(xiàn)問(wèn)題,定時(shí)任務(wù)會(huì)撈取未執(zhí)行完的任務(wù),繼續(xù)未執(zhí)行完的任務(wù),直到執(zhí)行完成為止,或者取消已經(jīng)完成的部分操作回到原始狀態(tài)。這種方法在任務(wù)完成每個(gè)階段的時(shí)候,都要更新數(shù)據(jù)庫(kù)中任務(wù)的狀態(tài),這在大規(guī)模高并發(fā)系統(tǒng)中不會(huì)有太好的性能,一個(gè)更好的辦法是用Write-Ahead Log(寫(xiě)前日志),這和數(shù)據(jù)庫(kù)的Bin Log(操作日志)相似,在做每一個(gè)操作步驟,都先寫(xiě)入日志,如果操作遇到問(wèn)題而停止的時(shí)候,可以讀取日志按照步驟進(jìn)行恢復(fù),并且繼續(xù)執(zhí)行未完成的工作,最后達(dá)到一致。寫(xiě)前日志可以利用機(jī)械硬盤(pán)的追加寫(xiě)而達(dá)到較好性能,因此,這是一種專(zhuān)業(yè)化的實(shí)現(xiàn)方式,多數(shù)業(yè)務(wù)系系統(tǒng)還是使用數(shù)據(jù)庫(kù)記錄的字段來(lái)記錄任務(wù)的執(zhí)行狀態(tài),也就是記錄中間的“軟狀態(tài)”,一個(gè)任務(wù)的狀態(tài)流轉(zhuǎn)一般可以通過(guò)數(shù)據(jù)庫(kù)的行級(jí)鎖來(lái)實(shí)現(xiàn),這比使用Write-Ahead Log實(shí)現(xiàn)更簡(jiǎn)單、更快速。
有了BASE理論作為基礎(chǔ),我們對(duì)復(fù)雜的分布式事務(wù)進(jìn)行拆解,對(duì)其中的每一步驟都記錄其狀態(tài),有問(wèn)題的時(shí)候可以根據(jù)記錄的狀態(tài)來(lái)繼續(xù)執(zhí)行任務(wù),達(dá)到最終的一致,通過(guò)這個(gè)方法我們可以解決案例2-轉(zhuǎn)賬和案例3-下訂單和扣庫(kù)存中遇到的問(wèn)題。
4. 酸堿平衡的總結(jié)
1.使用向上擴(kuò)展(強(qiáng)悍的硬件)運(yùn)行專(zhuān)業(yè)的關(guān)系型數(shù)據(jù)庫(kù)(例如:Oracle或者DB2)能夠保證強(qiáng)一致性,錢(qián)能解決的問(wèn)題就不是問(wèn)題。
2.如果錢(qián)是問(wèn)題,可以對(duì)廉價(jià)硬件運(yùn)行的開(kāi)源關(guān)系型數(shù)據(jù)庫(kù)(例如:MySQL)進(jìn)行分片,將相關(guān)的數(shù)據(jù)分到數(shù)據(jù)庫(kù)的同一個(gè)片,仍然能夠使用關(guān)系型數(shù)據(jù)庫(kù)保證事務(wù)。
3.如果業(yè)務(wù)規(guī)則限制,無(wú)法將相關(guān)的數(shù)據(jù)分到同一個(gè)片,就需要實(shí)現(xiàn)最終一致性,通過(guò)記錄事務(wù)的軟狀態(tài)(中間狀態(tài)、臨時(shí)狀態(tài)),一旦處于不一致,可以通過(guò)系統(tǒng)自動(dòng)化或者人工干預(yù)來(lái)修復(fù)不一致的情況。
分布式一致性協(xié)議
國(guó)際開(kāi)放標(biāo)準(zhǔn)組織Open Group定義了DTS(分布式事務(wù)處理模型),模型中包含4個(gè)角色:應(yīng)用程序、事務(wù)管理器、資源管理器、通信資源管理器四部分。事務(wù)處理器是統(tǒng)管全局的管理者,資源處理器和通信資源處理器是事務(wù)的參與者。
J2EE規(guī)范也包含此分布式事務(wù)處理模型的規(guī)范,并在所有的AppServer中進(jìn)行實(shí)現(xiàn),J2EE規(guī)范中定義了TX協(xié)議和XA協(xié)議,TX協(xié)議定義應(yīng)用程序與事務(wù)管理器之間的接口,而XA協(xié)議定義了事務(wù)管理器與資源處理器之間的接口,在過(guò)去,大家使用AppServer,例如:Websphere、Weblogic、Jboss等配置數(shù)據(jù)源的時(shí)候會(huì)看見(jiàn)類(lèi)似XADatasource的數(shù)據(jù)源,這就是實(shí)現(xiàn)了DTS的關(guān)系型數(shù)據(jù)庫(kù)的數(shù)據(jù)源。企業(yè)級(jí)開(kāi)發(fā)JEE中,關(guān)系型數(shù)據(jù)庫(kù)、JMS服務(wù)扮演資源管理器的角色,而EJB容器則扮演事務(wù)管理器的角色。
下面我們就介紹兩階段提交協(xié)議、三階段提交協(xié)議以及阿里巴巴提出的TCC,它們都是根據(jù)DTS這一思想演變出來(lái)的。
1. 兩階段提交協(xié)議
上面描述的JEE的XA協(xié)議就是根據(jù)兩階段提交來(lái)保證事務(wù)的完整性,并實(shí)現(xiàn)分布式服務(wù)化的強(qiáng)一致性。
兩階段提交協(xié)議把分布式事務(wù)分成兩個(gè)過(guò)程,一個(gè)是準(zhǔn)備階段,一個(gè)是提交階段,準(zhǔn)備階段和提交階段都是由事務(wù)管理器發(fā)起的,為了接下來(lái)講解方便,我們把事務(wù)管理器稱(chēng)為協(xié)調(diào)者,把資管管理器稱(chēng)為參與者。兩階段如下:
準(zhǔn)備階段:協(xié)調(diào)者向參與者發(fā)起指令,參與者評(píng)估自己的狀態(tài),如果參與者評(píng)估指令可以完成,參與者會(huì)寫(xiě)redo或者undo日志(這也是前面提起的Write-Ahead Log的一種),然后鎖定資源,執(zhí)行操作,但是并不提交。
提交階段:如果每個(gè)參與者明確返回準(zhǔn)備成功,也就是預(yù)留資源和執(zhí)行操作成功,協(xié)調(diào)者向參與者發(fā)起提交指令,參與者提交資源變更的事務(wù),釋放鎖定的資源;如果任何一個(gè)參與者明確返回準(zhǔn)備失敗,也就是預(yù)留資源或者執(zhí)行操作失敗,協(xié)調(diào)者向參與者發(fā)起中止指令,參與者取消已經(jīng)變更的事務(wù),執(zhí)行undo日志,釋放鎖定的資源。
兩階段提交協(xié)議成功場(chǎng)景示意圖如下:
我們看到兩階段提交協(xié)議在準(zhǔn)備階段鎖定資源,是一個(gè)重量級(jí)的操作,并能保證強(qiáng)一致性,但是實(shí)現(xiàn)起來(lái)復(fù)雜、成本較高,不夠靈活,更重要的是它有如下致命的問(wèn)題:
1.阻塞:從上面的描述來(lái)看,對(duì)于任何一次指令必須收到明確的響應(yīng),才會(huì)繼續(xù)做下一步,否則處于阻塞狀態(tài),占用的資源被一直鎖定,不會(huì)被釋放。
2.單點(diǎn)故障:如果協(xié)調(diào)者宕機(jī),參與者沒(méi)有了協(xié)調(diào)者指揮,會(huì)一直阻塞,盡管可以通過(guò)選舉新的協(xié)調(diào)者替代原有協(xié)調(diào)者,但是如果之前協(xié)調(diào)者在發(fā)送一個(gè)提交指令后宕機(jī),而提交指令僅僅被一個(gè)參與者接受,并且參與者接收后也宕機(jī),新上任的協(xié)調(diào)者無(wú)法處理這種情況。
3.腦裂:協(xié)調(diào)者發(fā)送提交指令,有的參與者接收到執(zhí)行了事務(wù),有的參與者沒(méi)有接收到事務(wù),就沒(méi)有執(zhí)行事務(wù),多個(gè)參與者之間是不一致的。
上面所有的這些問(wèn)題,都是需要人工干預(yù)處理,沒(méi)有自動(dòng)化的解決方案,因此兩階段提交協(xié)議在正常情況下能保證系統(tǒng)的強(qiáng)一致性,但是在出現(xiàn)異常情況下,當(dāng)前處理的操作處于錯(cuò)誤狀態(tài),需要管理員人工干預(yù)解決,因此可用性不夠好,這也符合CAP協(xié)議的一致性和可用性不能兼得的原理。
2. 三階段提交協(xié)議
三階段提交協(xié)議是兩階段提交協(xié)議的改進(jìn)版本。它通過(guò)超時(shí)機(jī)制解決了阻塞的問(wèn)題,并且把兩個(gè)階段增加為三個(gè)階段:
詢(xún)問(wèn)階段:協(xié)調(diào)者詢(xún)問(wèn)參與者是否可以完成指令,協(xié)調(diào)者只需要回答是還是不是,而不需要做真正的操作,這個(gè)階段超時(shí)導(dǎo)致中止。
準(zhǔn)備階段:如果在詢(xún)問(wèn)階段所有的參與者都返回可以執(zhí)行操作,協(xié)調(diào)者向參與者發(fā)送預(yù)執(zhí)行請(qǐng)求,然后參與者寫(xiě)redo和undo日志,執(zhí)行操作,但是不提交操作;如果在詢(xún)問(wèn)階段任何參與者返回不能執(zhí)行操作的結(jié)果,則協(xié)調(diào)者向參與者發(fā)送中止請(qǐng)求,這里的邏輯與兩階段提交協(xié)議的的準(zhǔn)備階段是相似的,這個(gè)階段超時(shí)導(dǎo)致成功。
提交階段:如果每個(gè)參與者在準(zhǔn)備階段返回準(zhǔn)備成功,也就是預(yù)留資源和執(zhí)行操作成功,協(xié)調(diào)者向參與者發(fā)起提交指令,參與者提交資源變更的事務(wù),釋放鎖定的資源;如果任何一個(gè)參與者返回準(zhǔn)備失敗,也就是預(yù)留資源或者執(zhí)行操作失敗,協(xié)調(diào)者向參與者發(fā)起中止指令,參與者取消已經(jīng)變更的事務(wù),執(zhí)行undo日志,釋放鎖定的資源,這里的邏輯與兩階段提交協(xié)議的提交階段一致。
三階段提交協(xié)議成功場(chǎng)景示意圖如下:
然而,這里與兩階段提交協(xié)議有兩個(gè)主要的不同:
增加了一個(gè)詢(xún)問(wèn)階段,詢(xún)問(wèn)階段可以確保盡可能早的發(fā)現(xiàn)無(wú)法執(zhí)行操作而需要中止的行為,但是它并不能發(fā)現(xiàn)所有的這種行為,只會(huì)減少這種情況的發(fā)生。
在準(zhǔn)備階段以后,協(xié)調(diào)者和參與者執(zhí)行的任務(wù)中都增加了超時(shí),一旦超時(shí),協(xié)調(diào)者和參與者都繼續(xù)提交事務(wù),默認(rèn)為成功,這也是根據(jù)概率統(tǒng)計(jì)上超時(shí)后默認(rèn)成功的正確性最大。
三階段提交協(xié)議與兩階段提交協(xié)議相比,具有如上的優(yōu)點(diǎn),但是一旦發(fā)生超時(shí),系統(tǒng)仍然會(huì)發(fā)生不一致,只不過(guò)這種情況很少見(jiàn)罷了,好處就是至少不會(huì)阻塞和永遠(yuǎn)鎖定資源。
3. TCC
上面兩節(jié)講解了兩階段提交協(xié)議和三階段提交協(xié)議,實(shí)際上他們能解決案例2-轉(zhuǎn)賬和案例3-下訂單和扣庫(kù)存中的分布式事務(wù)的問(wèn)題,但是遇到極端情況,系統(tǒng)會(huì)發(fā)生阻塞或者不一致的問(wèn)題,需要運(yùn)營(yíng)或者技術(shù)人工解決。無(wú)論兩階段還是三階段方案中都包含多個(gè)參與者、多個(gè)階段實(shí)現(xiàn)一個(gè)事務(wù),實(shí)現(xiàn)復(fù)雜,性能也是一個(gè)很大的問(wèn)題,因此,在互聯(lián)網(wǎng)高并發(fā)系統(tǒng)中,鮮有使用兩階段提交和三階段提交協(xié)議的場(chǎng)景。
阿里巴巴提出了新的TCC協(xié)議,TCC協(xié)議將一個(gè)任務(wù)拆分成Try、Confirm、Cancel,正常的流程會(huì)先執(zhí)行Try,如果執(zhí)行沒(méi)有問(wèn)題,再執(zhí)行Confirm,如果執(zhí)行過(guò)程中出了問(wèn)題,則執(zhí)行操作的逆操Cancel,從正常的流程上講,這仍然是一個(gè)兩階段的提交協(xié)議,但是,在執(zhí)行出現(xiàn)問(wèn)題的時(shí)候,有一定的自我修復(fù)能力,如果任何一個(gè)參與者出現(xiàn)了問(wèn)題,協(xié)調(diào)者通過(guò)執(zhí)行操作的逆操作來(lái)取消之前的操作,達(dá)到最終的一致?tīng)顟B(tài)。
可以看出,從時(shí)序上,如果遇到極端情況下TCC會(huì)有很多問(wèn)題的,例如,如果在Cancel的時(shí)候一些參與者收到指令,而一些參與者沒(méi)有收到指令,整個(gè)系統(tǒng)仍然是不一致的,這種復(fù)雜的情況,系統(tǒng)首先會(huì)通過(guò)補(bǔ)償?shù)姆绞剑瑖L試自動(dòng)修復(fù)的,如果系統(tǒng)無(wú)法修復(fù),必須由人工參與解決。
從TCC的邏輯上看,可以說(shuō)TCC是簡(jiǎn)化版的三階段提交協(xié)議,解決了兩階段提交協(xié)議的阻塞問(wèn)題,但是沒(méi)有解決極端情況下會(huì)出現(xiàn)不一致和腦裂的問(wèn)題。然而,TCC通過(guò)自動(dòng)化補(bǔ)償手段,會(huì)把需要人工處理的不一致情況降到到最少,也是一種非常有用的解決方案,根據(jù)線人,阿里在內(nèi)部的一些中間件上實(shí)現(xiàn)了TCC模式。
我們給出一個(gè)使用TCC的實(shí)際案例,在秒殺的場(chǎng)景,用戶(hù)發(fā)起下單請(qǐng)求,應(yīng)用層先查詢(xún)庫(kù)存,確認(rèn)商品庫(kù)存還有余量,則鎖定庫(kù)存,此時(shí)訂單狀態(tài)為待支付,然后指引用戶(hù)去支付,由于某種原因用戶(hù)支付失敗,或者支付超時(shí),系統(tǒng)會(huì)自動(dòng)將鎖定的庫(kù)存解鎖供其他用戶(hù)秒殺。
TCC協(xié)議使用場(chǎng)景示意圖如下:
總結(jié)一下,兩階段提交協(xié)議、三階段提交協(xié)議、TCC協(xié)議都能保證分布式事務(wù)的一致性,他們保證的分布式系統(tǒng)的一致性從強(qiáng)到弱,TCC達(dá)到的目標(biāo)是最終一致性,其中任何一種方法都可以不同程度的解決案例2:轉(zhuǎn)賬、案例3:下訂單和扣庫(kù)存的問(wèn)題,只是實(shí)現(xiàn)的一致性的級(jí)別不一樣而已,對(duì)于案例4:同步超時(shí)可以通過(guò)TCC的理念解決,如果同步調(diào)用超時(shí),調(diào)用方可以使用fastfail策略,返回調(diào)用方的使用方失敗的結(jié)果,同時(shí)調(diào)用服務(wù)的逆向cancel操作,保證服務(wù)的最終一致性。
4. 保證最終一致性的模式
在大規(guī)模高并發(fā)服務(wù)化系統(tǒng)中,一個(gè)功能被拆分成多個(gè)具有單一功能的元功能,一個(gè)流程會(huì)有多個(gè)系統(tǒng)的多個(gè)元功能組合實(shí)現(xiàn),如果使用兩階段提交協(xié)議和三階段提交協(xié)議,確實(shí)能解決系統(tǒng)間一致性問(wèn)題,除了這兩個(gè)協(xié)議帶來(lái)的自身的問(wèn)題,這些協(xié)議的實(shí)現(xiàn)比較復(fù)雜、成本比較高,最重要的是性能并不好,相比來(lái)看,TCC協(xié)議更簡(jiǎn)單、容易實(shí)現(xiàn),但是TCC協(xié)議由于每個(gè)事務(wù)都需要執(zhí)行Try,再執(zhí)行Confirm,略微顯得臃腫,因此,在現(xiàn)實(shí)的系統(tǒng)中,底線要求僅僅需要能達(dá)到最終一致性,而不需要實(shí)現(xiàn)專(zhuān)業(yè)的、復(fù)雜的一致性協(xié)議,實(shí)現(xiàn)最終一致性有一些非常有效的、簡(jiǎn)單粗暴的模式,下面就介紹這些模式及其應(yīng)用場(chǎng)景。
1) 查詢(xún)模式
任何一個(gè)服務(wù)操作都需要提供一個(gè)查詢(xún)接口,用來(lái)向外部輸出操作執(zhí)行的狀態(tài)。服務(wù)操作的使用方可以通過(guò)查詢(xún)接口,得知服務(wù)操作執(zhí)行的狀態(tài),然后根據(jù)不同狀態(tài)來(lái)做不同的處理操作。
為了能夠?qū)崿F(xiàn)查詢(xún),每個(gè)服務(wù)操作都需要有唯一的流水號(hào)標(biāo)識(shí),也可使用此次服務(wù)操作對(duì)應(yīng)的資源ID來(lái)標(biāo)志,例如:請(qǐng)求流水號(hào)、訂單號(hào)等。
首先,單筆查詢(xún)操作是必須提供的,我們也鼓勵(lì)使用單筆訂單查詢(xún),這是因?yàn)槊看握{(diào)用需要占用的負(fù)載是可控的,批量查詢(xún)則根據(jù)需要來(lái)提供,如果使用了批量查詢(xún),需要有合理的分頁(yè)機(jī)制,并且必須限制分頁(yè)的大小,以及對(duì)批量查詢(xún)的QPS需要有容量評(píng)估和流控等。
查詢(xún)模式的示意圖如下:
對(duì)于案例4:同步超時(shí)、案例5:異步回調(diào)超時(shí)、案例6:掉單、案例7:系統(tǒng)間狀態(tài)不一致,我們都需要使用查詢(xún)模式來(lái)了解被調(diào)用服務(wù)的處理情況,來(lái)決定下一步做什么:補(bǔ)償未完成的操作還是回滾已經(jīng)完成的操作。
2) 補(bǔ)償模式
有了上面的查詢(xún)模式,在任何情況下,我們都能得知具體的操作所處的狀態(tài),如果整個(gè)操作處于不正常的狀態(tài),我們需要修正操作中有問(wèn)題的子操作,這可能需要重新執(zhí)行未完成的子操作,后者取消已經(jīng)完成的子操作,通過(guò)修復(fù)使整個(gè)分布式系統(tǒng)達(dá)到一致,為了讓系統(tǒng)最終一致而做的努力都叫做補(bǔ)償。
對(duì)于服務(wù)化系統(tǒng)中同步調(diào)用的操作,業(yè)務(wù)操作發(fā)起的主動(dòng)方在還沒(méi)有得到業(yè)務(wù)操作執(zhí)行方的明確返回或者調(diào)用超時(shí),場(chǎng)景可參考案例4:同步超時(shí),這個(gè)時(shí)候業(yè)務(wù)發(fā)起的主動(dòng)方需要及時(shí)的調(diào)用業(yè)務(wù)執(zhí)行方獲得操作執(zhí)行的狀態(tài),這里使用查詢(xún)模式,獲得業(yè)務(wù)操作的執(zhí)行方的狀態(tài)后,如果業(yè)務(wù)執(zhí)行方已經(jīng)完預(yù)設(shè)的工作,則業(yè)務(wù)發(fā)起方給業(yè)務(wù)的使用方返回成功,如果業(yè)務(wù)操作的執(zhí)行方的狀態(tài)為失敗或者未知,則會(huì)立即告訴業(yè)務(wù)的使用方失敗,然后調(diào)用業(yè)務(wù)操作的逆向操作,保證操作不被執(zhí)行或者回滾已經(jīng)執(zhí)行的操作,讓業(yè)務(wù)的使用方、業(yè)務(wù)發(fā)起的主動(dòng)方、業(yè)務(wù)的操作方最終達(dá)成一致的狀態(tài)。
補(bǔ)償模式的示意圖如下:
補(bǔ)償操作根據(jù)發(fā)起形式分為:
1.自動(dòng)恢復(fù):程序根據(jù)發(fā)生不一致的環(huán)境,通過(guò)繼續(xù)未完成的操作,或者回滾已經(jīng)完成的操作,自動(dòng)來(lái)達(dá)到一致。
2.通知運(yùn)營(yíng):如果程序無(wú)法自動(dòng)恢復(fù),并且設(shè)計(jì)時(shí)考慮到了不一致的場(chǎng)景,可以提供運(yùn)營(yíng)功能,通過(guò)運(yùn)營(yíng)手工進(jìn)行補(bǔ)償。
3.通知技術(shù):如果很不巧,系統(tǒng)無(wú)法自動(dòng)回復(fù),又沒(méi)有運(yùn)營(yíng)功能,那必須通過(guò)技術(shù)手段來(lái)解決,技術(shù)手段包括走數(shù)據(jù)庫(kù)變更或者代碼變更來(lái)解決,這是最糟的一種場(chǎng)景。
3) 異步確保模式
異步確保模式是補(bǔ)償模式的一個(gè)典型案例,經(jīng)常應(yīng)用到使用方對(duì)響應(yīng)時(shí)間要求并不太高,我們通常把這類(lèi)操作從主流程中摘除,通過(guò)異步的方式進(jìn)行處理,處理后把結(jié)果通過(guò)通知系統(tǒng)通知給使用方,這個(gè)方案最大的好處能夠?qū)Ω卟l(fā)流量進(jìn)行消峰,例如:電商系統(tǒng)中的物流、配送,以及支付系統(tǒng)中的計(jì)費(fèi)、入賬等。
實(shí)踐中,將要執(zhí)行的異步操作封裝后持久入庫(kù),然后通過(guò)定時(shí)撈取未完成的任務(wù)進(jìn)行補(bǔ)償操作來(lái)實(shí)現(xiàn)異步確保模式,只要定時(shí)系統(tǒng)足夠健壯,任何一個(gè)任務(wù)最終會(huì)被成功執(zhí)行。
異步確保模式的示意圖如下:
對(duì)于案例5:異步回調(diào)超時(shí),使用的就是異步確保模式,這種情況下對(duì)于某個(gè)操作,如果遲遲沒(méi)有收到響應(yīng),我們通過(guò)查詢(xún)模式和補(bǔ)償模式來(lái)繼續(xù)未完成的操作。
4. 定期校對(duì)模式
既然我們?cè)谙到y(tǒng)中實(shí)現(xiàn)最終一致性,系統(tǒng)在沒(méi)有達(dá)到一致之前,系統(tǒng)間的狀態(tài)是不一致的,甚至是混亂的,需要補(bǔ)償操作來(lái)達(dá)到一致的目的,但是我們?nèi)绾蝸?lái)發(fā)現(xiàn)需要補(bǔ)償?shù)牟僮髂兀?/span>
在操作的主流程中的系統(tǒng)間執(zhí)行校對(duì)操作,我們可以事后異步的批量校對(duì)操作的狀態(tài),如果發(fā)現(xiàn)不一致的操作,則進(jìn)行補(bǔ)償,補(bǔ)償操作與補(bǔ)償模式中的補(bǔ)償操作是一致的。
另外,實(shí)現(xiàn)定期校對(duì)的一個(gè)關(guān)鍵就是分布式系統(tǒng)中需要有一個(gè)自始至終唯一的ID,ID的生成請(qǐng)參考SnowFlake。
在分布式系統(tǒng)中,全局唯一ID的示意圖如下:
一般情況下,生成全局唯一ID有兩種方法:
持久型:使用數(shù)據(jù)庫(kù)表自增字段或者Sequence生成,為了提高效率,每個(gè)應(yīng)用節(jié)點(diǎn)可以緩存一批次的ID,如果機(jī)器重啟可能會(huì)損失一部分ID,但是這并不會(huì)產(chǎn)生任何問(wèn)題。
時(shí)間型:一般由機(jī)器號(hào)、業(yè)務(wù)號(hào)、時(shí)間、單節(jié)點(diǎn)內(nèi)自增ID組成,由于時(shí)間一般精確到秒或者毫秒,因此不需要持久就能保證在分布式系統(tǒng)中全局唯一、粗略遞增能特點(diǎn)。
實(shí)踐中,為了能在分布式系統(tǒng)中迅速的定位問(wèn)題,一般的分布式系統(tǒng)都有技術(shù)支持系統(tǒng),它能夠跟蹤一個(gè)請(qǐng)求的調(diào)用鏈,調(diào)用鏈?zhǔn)窃诙S的維度跟蹤一個(gè)調(diào)用請(qǐng)求,最后形成一個(gè)調(diào)用樹(shù),原理可參考谷歌的論文Dapper, a Large-Scale Distributed Systems Tracing Infrastructure,一個(gè)開(kāi)源的參考實(shí)現(xiàn)為pinpoint。
在分布式系統(tǒng)中,調(diào)用鏈的示意圖如下:
全局的唯一流水ID可以把一個(gè)請(qǐng)求在分布式系統(tǒng)中的流轉(zhuǎn)的路徑聚合,而調(diào)用鏈中的spanid可以把聚合的請(qǐng)求路徑通過(guò)樹(shù)形結(jié)構(gòu)進(jìn)行展示,讓技術(shù)支持人員輕松的發(fā)現(xiàn)系統(tǒng)出現(xiàn)的問(wèn)題,能夠快速定位出現(xiàn)問(wèn)題的服務(wù)節(jié)點(diǎn),提高應(yīng)急效率。
關(guān)于訂單跟蹤、調(diào)用鏈跟蹤、業(yè)務(wù)鏈跟蹤,我們會(huì)在后續(xù)文章中詳細(xì)介紹。
在分布式系統(tǒng)中構(gòu)建了唯一ID,調(diào)用鏈等基礎(chǔ)設(shè)施,我們很容易對(duì)系統(tǒng)間的不一致進(jìn)行核對(duì),通常我們需要構(gòu)建第三方的定期核對(duì)系統(tǒng),以第三方的角度來(lái)監(jiān)控服務(wù)執(zhí)行的健康程度。
定期核對(duì)系統(tǒng)示意圖如下:
對(duì)于案例6:掉單、案例7:系統(tǒng)間狀態(tài)不一致通常通過(guò)定期校對(duì)模式發(fā)現(xiàn)問(wèn)題,并通過(guò)補(bǔ)償模式來(lái)修復(fù),最后完成系統(tǒng)間的最終一致性。
定期校對(duì)模式多應(yīng)用在金融系統(tǒng),金融系統(tǒng)由于涉及到資金安全,需要保證百分之百的準(zhǔn)確性,所以,需要多重的一致性保證機(jī)制,包括:系統(tǒng)間的一致性對(duì)賬、現(xiàn)金對(duì)賬、賬務(wù)對(duì)賬、手續(xù)費(fèi)對(duì)賬等等,這些都屬于定期校對(duì)模式,順便說(shuō)一下,金融系統(tǒng)與社交應(yīng)用在技術(shù)上本質(zhì)的區(qū)別在于社交應(yīng)用在于量大,而金融系統(tǒng)在于數(shù)據(jù)的準(zhǔn)確性。
到現(xiàn)在為止,我們看到通過(guò)查詢(xún)模式、補(bǔ)償模式、定期核對(duì)模式可以解決案例4到案例7的所有問(wèn)題,對(duì)于案例4:同步超時(shí),如果同步超時(shí),我們需要查詢(xún)狀態(tài)進(jìn)行補(bǔ)償,對(duì)于案例5:異步回調(diào)超時(shí),如果遲遲沒(méi)有收到回調(diào)響應(yīng),我們也會(huì)通過(guò)查詢(xún)狀態(tài)進(jìn)行補(bǔ)償,對(duì)于案例6:掉單、案例7:系統(tǒng)間狀態(tài)不一致,我們通過(guò)定期核對(duì)模式可以保證系統(tǒng)間操作的一致性,避免掉單和狀態(tài)不一致導(dǎo)致問(wèn)題。
5. 可靠消息模式
在分布式系統(tǒng)中,對(duì)于主流程中優(yōu)先級(jí)比較低的操作,大多采用異步的方式執(zhí)行,也就是前面提到的異步確保型,為了讓異步操作的調(diào)用方和被調(diào)用方充分的解耦,也由于專(zhuān)業(yè)的消息隊(duì)列本身具有可伸縮、可分片、可持久等功能,我們通常通過(guò)消息隊(duì)列實(shí)現(xiàn)異步化,對(duì)于消息隊(duì)列,我們需要建立特殊的設(shè)施保證可靠的消息發(fā)送以及處理機(jī)的冪等等。
消息的可靠發(fā)送
消息的可靠發(fā)送可以認(rèn)為是盡最大努力發(fā)送消息通知,有兩種實(shí)現(xiàn)方法:
第一種,發(fā)送消息之前,把消息持久到數(shù)據(jù)庫(kù),狀態(tài)標(biāo)記為待發(fā)送,然后發(fā)送消息,如果發(fā)送成功,將消息改為發(fā)送成功。定時(shí)任務(wù)定時(shí)從數(shù)據(jù)庫(kù)撈取一定時(shí)間內(nèi)未發(fā)送的消息,將消息發(fā)送。
第二種,實(shí)現(xiàn)方式與第一種類(lèi)似,不同的是持久消息的數(shù)據(jù)庫(kù)是獨(dú)立的,并不耦合在業(yè)務(wù)系統(tǒng)中。發(fā)送消息之前,先發(fā)送一個(gè)預(yù)消息給某一個(gè)第三方的消息管理器,消息管理器將其持久到數(shù)據(jù)庫(kù),并標(biāo)記狀態(tài)為待發(fā)送,發(fā)送成功后,標(biāo)記消息為發(fā)送成功。定時(shí)任務(wù)定時(shí)從數(shù)據(jù)庫(kù)撈取一定時(shí)間內(nèi)未發(fā)送的消息,回查業(yè)務(wù)系統(tǒng)是否要繼續(xù)發(fā)送,根據(jù)查詢(xún)結(jié)果來(lái)確定消息的狀態(tài)。
一些公司把消息的可靠發(fā)送實(shí)現(xiàn)在了中間件里,通過(guò)Spring的注入,在消息發(fā)送的時(shí)候自動(dòng)持久消息記錄,如果有消息記錄沒(méi)有發(fā)送成功,定時(shí)會(huì)補(bǔ)償發(fā)送。
消息處理器的冪等性
如果我們要保證消息可靠的發(fā)送,簡(jiǎn)單來(lái)說(shuō),要保證消息一定要發(fā)送出去,那么就需要有重試機(jī)制,有了重試機(jī)制,消息一定會(huì)重復(fù),那么我們需要對(duì)重復(fù)做處理。
處理重復(fù)的最佳方式為保證操作的冪等性,冪等性的數(shù)學(xué)公式為:
f(f(x)) = f(x)
保證操作的冪等性常用的幾個(gè)方法:
1.使用數(shù)據(jù)庫(kù)表的唯一鍵進(jìn)行濾重,拒絕重復(fù)的請(qǐng)求。
2.使用分布式表對(duì)請(qǐng)求進(jìn)行濾重。
3.使用狀態(tài)流轉(zhuǎn)的方向性來(lái)濾重,通常使用行級(jí)鎖來(lái)實(shí)現(xiàn)。
4.根據(jù)業(yè)務(wù)的特點(diǎn),操作本身就是冪等的,例如:刪除一個(gè)資源、增加一個(gè)資源、獲得一個(gè)資源等。
6. 緩存一致性模型
大規(guī)模高并發(fā)系統(tǒng)中一個(gè)常見(jiàn)的核心需求就是億級(jí)的讀需求,顯然,關(guān)系型數(shù)據(jù)庫(kù)并不是解決高并發(fā)讀需求的最佳方案,互聯(lián)網(wǎng)的經(jīng)典做法就是使用緩存抗讀需求,下面有一些使用緩存的保證一致性的最佳實(shí)踐:
如果性能要求不是非常的高,盡量使用分布式緩存,而不要使用本地緩存。
種緩存的時(shí)候一定種完全,如果緩存數(shù)據(jù)的一部分有效,一部分無(wú)效,寧可放棄種緩存,也不要把部分?jǐn)?shù)據(jù)種入緩存。
數(shù)據(jù)庫(kù)與緩存只需要保持弱一致性,而不需要強(qiáng)一致性,讀的順序要先緩存,后數(shù)據(jù)庫(kù),寫(xiě)的順序要先數(shù)據(jù)庫(kù),后緩存。
這里的最佳實(shí)踐能夠解決案例8:緩存和數(shù)據(jù)庫(kù)不一致、案例9:本地緩存節(jié)點(diǎn)間不一致、案例10:緩存數(shù)據(jù)結(jié)構(gòu)不一致的問(wèn)題,對(duì)于數(shù)據(jù)存儲(chǔ)層、緩存與數(shù)據(jù)庫(kù)、Nosql等的一致性是更深入的存儲(chǔ)一致性技術(shù),將會(huì)在后續(xù)文章單獨(dú)介紹,這里的數(shù)據(jù)一致性主要是處理應(yīng)用層與緩存、應(yīng)用層與數(shù)據(jù)庫(kù)、一部分的緩存與數(shù)據(jù)庫(kù)的一致性。
專(zhuān)題模式
這一節(jié)介紹特殊場(chǎng)景下的一致性問(wèn)題和解決方案。
遷移開(kāi)關(guān)的設(shè)計(jì)
在大多數(shù)企業(yè)里,新項(xiàng)目和老項(xiàng)目一般會(huì)共存,大家都在努力的下掉老項(xiàng)目,但是由于種種原因總是下不掉,如果要徹底的下掉老項(xiàng)目,就必須要有非常完善的遷移方案,遷移是一項(xiàng)非常復(fù)雜而艱巨的任務(wù),我會(huì)在將來(lái)的文章中詳細(xì)探討遷移方案、流程和技術(shù),這里我們只對(duì)遷移中使用的開(kāi)關(guān)進(jìn)行描述。
遷移過(guò)程必須使用開(kāi)關(guān),開(kāi)關(guān)一般都會(huì)基于多個(gè)維度來(lái)設(shè)計(jì),例如:全局的、用戶(hù)的、角色的、商戶(hù)的、產(chǎn)品的等等,如果遷移過(guò)程中遇到問(wèn)題,我們需要關(guān)閉開(kāi)關(guān),遷移回老的系統(tǒng),這需要我們的新系統(tǒng)兼容老的數(shù)據(jù),老的系統(tǒng)也兼容新的數(shù)據(jù),從某種意義上來(lái)講,遷移比實(shí)現(xiàn)新系統(tǒng)更加困難。
曾經(jīng)看過(guò)很多簡(jiǎn)單的開(kāi)關(guān)設(shè)計(jì),有的開(kāi)關(guān)設(shè)計(jì)在應(yīng)用層次,通過(guò)一個(gè)curl語(yǔ)句調(diào)用,沒(méi)有權(quán)限控制,這樣的開(kāi)關(guān)在服務(wù)池的每個(gè)節(jié)點(diǎn)都是不同步的、不一致的;還有的系統(tǒng)把開(kāi)關(guān)配置放在中心化的配置系統(tǒng)、數(shù)據(jù)庫(kù)或者緩存等,處理的每個(gè)請(qǐng)求都通過(guò)統(tǒng)一的開(kāi)關(guān)來(lái)判斷是否遷移等等,這樣的開(kāi)關(guān)有一個(gè)致命的缺點(diǎn),服務(wù)請(qǐng)求在處理過(guò)程中,開(kāi)關(guān)可能會(huì)變化,各個(gè)節(jié)點(diǎn)之間開(kāi)關(guān)可能不同步、不一致,導(dǎo)致重復(fù)的請(qǐng)求可能走到新的邏輯又走了老的邏輯,如果新的邏輯和老的邏輯沒(méi)有保證冪等性,這個(gè)請(qǐng)求就被重復(fù)處理了,如果是金融行業(yè)的應(yīng)用,可能會(huì)導(dǎo)致資金損失,電商系統(tǒng)可能會(huì)導(dǎo)致發(fā)貨并退款等問(wèn)題。
這里面我們推薦使用訂單開(kāi)關(guān),不管我們?cè)谑裁淳S度上設(shè)計(jì)了開(kāi)關(guān),接收到服務(wù)請(qǐng)求后,我們?cè)谡?qǐng)求創(chuàng)建的關(guān)聯(lián)實(shí)體(例如:訂單)上標(biāo)記開(kāi)關(guān),以后的任何處理流程,包括同步的和異步的處理流程,都通過(guò)訂單上的開(kāi)關(guān)來(lái)判斷,而不是通過(guò)全局的或者基于配置的開(kāi)關(guān),這樣在訂單創(chuàng)建的時(shí)候,開(kāi)關(guān)已經(jīng)確定,不再變更,一旦一份數(shù)據(jù)不再發(fā)生變化,那么它永遠(yuǎn)是線程安全的,并且不會(huì)有不一致的問(wèn)題。
這個(gè)模式在生產(chǎn)中使用比較頻繁,建議每個(gè)企業(yè)都把這個(gè)模式作為設(shè)計(jì)評(píng)審的一項(xiàng),如果不檢查這一項(xiàng),很多開(kāi)發(fā)童鞋都會(huì)偷懶,直接在配置中或者數(shù)據(jù)庫(kù)中做個(gè)開(kāi)關(guān)就上線了。
總結(jié)思考
本文從一致性問(wèn)題的實(shí)踐出發(fā),從大規(guī)模高并發(fā)服務(wù)化系統(tǒng)的實(shí)踐經(jīng)驗(yàn)中進(jìn)行總結(jié),列舉導(dǎo)致不一致的具體問(wèn)題,圍繞著具體問(wèn)題,總結(jié)出解決不一致的方法,并且抽象成模式,供大家在開(kāi)發(fā)服務(wù)化系統(tǒng)的過(guò)程中參考。
原文來(lái)自:聊聊架構(gòu)
免責(zé)聲明:以上內(nèi)容為本網(wǎng)站轉(zhuǎn)自其它媒體,相關(guān)信息僅為傳遞更多信息之目的,不代表本網(wǎng)觀點(diǎn),亦不代表本網(wǎng)站贊同其觀點(diǎn)或證實(shí)其內(nèi)容的真實(shí)性。