1 背景
一致性是一個抽象的、具有多重含義的計算機術(shù)語此再,在不同應用場景下昔搂,有不同的定義和含義。在傳統(tǒng)的IT時代输拇,一致性通常指強一致性摘符,強一致性通常體現(xiàn)在你中有我、我中有你、渾然一體逛裤;而在互聯(lián)網(wǎng)時代瘩绒,一致性的含義遠遠超出了它原有的含義,在我們討論互聯(lián)網(wǎng)時代的一致性之前别凹,我們先了解一下互聯(lián)網(wǎng)時代的特點草讶,互聯(lián)網(wǎng)時代信息量巨大、需要計算能力巨大炉菲,不但對用戶響應速度要求快堕战,而且吞吐量指標也要向外擴展(既:水平伸縮),于是單節(jié)點的服務器無法滿足需求拍霜,服務節(jié)點開始池化嘱丢,想想那個經(jīng)典的故事,一只筷子一折就斷祠饺,一把筷子怎么都折不斷越驻,可見人多力量大的思想是多么的重要,但是人多也不一定能解決所有事情道偷,還得進行有序缀旁、合理的分配任務,進行有效的管理勺鸦,于是互聯(lián)網(wǎng)時代談論最多的話題就是拆分并巍,拆分一般分為“水平拆分”和“垂直拆分”(大家不要對應到數(shù)據(jù)庫或者緩存拆分,這里主要表達一種邏輯)换途。這里懊渡,“水平拆分”指的是同一個功能由于單機節(jié)點無法滿足性能需求,需要擴展成為多節(jié)點军拟,多個節(jié)點具有一致的功能剃执,組成一個服務池,一個節(jié)點服務一部分的請求量懈息,團結(jié)起來共同處理大規(guī)模高并發(fā)的請求量肾档。“垂直拆分”指的是按照功能拆分漓拾,秉著“專業(yè)的人干專業(yè)的事兒”的原則阁最,把一個復雜的功能拆分到多個單一的簡單的元功能,不同的元功能組合在一起骇两,和未拆分前完成的功能是一致的速种,由于每個元功能職責單一、功能簡單低千,讓維護和變更都變得更簡單配阵、安全馏颂,更易于產(chǎn)品版本的迭代,在這樣的一個互聯(lián)網(wǎng)的時代和環(huán)境棋傍,一致性指分布式服務化系統(tǒng)之間的弱一致性救拉,包括應用系統(tǒng)一致性和數(shù)據(jù)一致性。
無論是水平拆分還是垂直拆分瘫拣,都解決了特定場景下的特定問題亿絮,凡事有好的一面,都會有壞的一面麸拄,拆分后的系統(tǒng)或者服務化的系統(tǒng)最大的問題就是一致性問題派昧,這么多個具有元功能的模塊,或者同一個功能池中的多個節(jié)點之間拢切,如何保證他們的信息是一致的蒂萎、工作步伐是一致的、狀態(tài)是一致的淮椰、互相協(xié)調(diào)有序的工作呢五慈?
本文根據(jù)作者在互聯(lián)網(wǎng)企業(yè)的實際項目經(jīng)驗,對服務化系統(tǒng)中最難解決的一致性問題進行研究和探討主穗,試圖從實踐經(jīng)驗中找到規(guī)律泻拦,抽象出模式,分享給大家忽媒,希望對大家的項目實施有所幫助聪轿,在對實踐的總結(jié)中也會對相關的一致性術(shù)語做最樸實的解釋,希望能幫助大家徹底理解一致性的本質(zhì)猾浦,并能將其應用到實踐,解決讀者現(xiàn)實中遇到的服務化系統(tǒng)的一致性問題灯抛,本文使用理論與實踐相結(jié)合的方法金赦,突出在實踐中解決問題的模式,因此叫做《分布式服務化系統(tǒng)一致性的“最佳實干”》对嚼。
2 問題
本節(jié)列舉不一致會導致的種種問題夹抗,這也包括一例生活中的問題。
案例1:買房
假如你想要享受生活的隨意纵竖,只想買個兩居漠烧,不想讓房貸有太大壓力,而你媳婦卻想要買個三居靡砌,還得帶花園的已脓,那么你們就不一致了,不一致導致生活不愉快通殃、不協(xié)調(diào)度液,嚴重情況下還會吵架,可見生活中的不一致問題影響很大。
案例2:轉(zhuǎn)賬
轉(zhuǎn)賬是經(jīng)典的不一致案例堕担,設想一下銀行為你處理一筆轉(zhuǎn)賬已慢,扣減你賬戶上的余額,然后增加別人賬戶的余額霹购;如果扣減你的賬戶余額成功佑惠,增加別人賬戶余額失敗,那么你就會損失這筆資金齐疙。反過來膜楷,如果扣減你的賬戶余額失敗,增加別人賬戶余額成功剂碴,那么銀行就會損失這筆資金把将,銀行需要賠付。對于資金處理系統(tǒng)來說忆矛,上面任何一種場景都是不允許發(fā)生的察蹲,一旦發(fā)生就會有資金損失,后果是不堪設想的催训,嚴重情況會讓一個公司瞬間倒閉洽议,可參考案例。
案例3:下訂單和扣庫存
電商系統(tǒng)中也有一個經(jīng)典的案例漫拭,下訂單和扣庫存如何保持一致亚兄,如果先下訂單,扣庫存失敗采驻,那么將會導致超賣审胚;如果下訂單沒有成功,扣庫存成功礼旅,那么會導致少賣膳叨。兩種情況都會導致運營成本的增加,嚴重情況下需要賠付痘系。
案例4:同步超時
服務化的系統(tǒng)間調(diào)用常常因為網(wǎng)絡問題導致系統(tǒng)間調(diào)用超時菲嘴,即使是網(wǎng)絡很好的機房,在億次流量的基數(shù)下汰翠,同步調(diào)用超時也是家常便飯龄坪。系統(tǒng)A同步調(diào)用系統(tǒng)B超時,系統(tǒng)A可以明確得到超時反饋复唤,但是無法確定系統(tǒng)B是否已經(jīng)完成了預定的功能或者沒有完成預定的功能健田。于是,系統(tǒng)A就迷茫了佛纫,不知道應該繼續(xù)做什么抄课,如何反饋給使用方唱星。(曾經(jīng)的一個B2B產(chǎn)品的客戶要求接口超時重新通知他們,這個在技術(shù)上是難以實現(xiàn)的跟磨,因為服務器本身可能并不知道自己超時间聊,可能會繼續(xù)正常的返回數(shù)據(jù),只是客戶端并沒有接受到結(jié)果罷了抵拘,因此這不是一個合理的解決方案)哎榴。
案例5:異步回調(diào)超時
此案例和上一個同步超時案例類似,不過這個場景使用了異步回調(diào)僵蛛,系統(tǒng)A同步調(diào)用系統(tǒng)B發(fā)起指令尚蝌,系統(tǒng)B采用受理模式,受理后則返回受理成功充尉,然后系統(tǒng)B異步通知系統(tǒng)A飘言。在這個過程中,如果系統(tǒng)A由于某種原因遲遲沒有收到回調(diào)結(jié)果驼侠,那么兩個系統(tǒng)間的狀態(tài)就不一致姿鸿,互相認知不同會導致系統(tǒng)間發(fā)生錯誤,嚴重情況下會影響核心事務倒源,甚至會導致資金損失苛预。
案例6:掉單
分布式系統(tǒng)中,兩個系統(tǒng)協(xié)作處理一個流程笋熬,分別為對方的上下游热某,如果一個系統(tǒng)中存在一個請求,通常指訂單胳螟,另外一個系統(tǒng)不存在昔馋,則導致掉單,掉單的后果很嚴重糖耸,有時候也會導致資金損失绒极。
案例7:系統(tǒng)間狀態(tài)不一致
這個案例與上面掉單案例類似,不同的是兩個系統(tǒng)間都存在請求蔬捷,但是請求的狀態(tài)不一致。
案例8:緩存和數(shù)據(jù)庫不一致
交易相關系統(tǒng)基本離不開關系型數(shù)據(jù)庫榔袋,依賴關系型數(shù)據(jù)庫提供的ACID特性(后面介紹)周拐,但是在大規(guī)模高并發(fā)的互聯(lián)網(wǎng)系統(tǒng)里,一些特殊的場景對讀的性能要求極高凰兑,服務于交易的數(shù)據(jù)庫難以抗住大規(guī)模的讀流量妥粟,通常需要在數(shù)據(jù)庫前墊緩存,那么緩存和數(shù)據(jù)庫之間的數(shù)據(jù)如何保持一致性吏够?是要保持強一致呢還是弱一致性呢勾给?
案例9:本地緩存節(jié)點間不一致
一個服務池上的多個節(jié)點為了滿足較高的性能需求滩报,需要使用本地緩存,使用了本地緩存播急,每個節(jié)點都會有一份緩存數(shù)據(jù)的拷貝脓钾,如果這些數(shù)據(jù)是靜態(tài)的、不變的桩警,那永遠都不會有問題可训,但是如果這些數(shù)據(jù)是半靜態(tài)的或者常被更新的,當被更新的時候捶枢,各個節(jié)點更新是有先后順序的握截,在更新的瞬間,各個節(jié)點的數(shù)據(jù)是不一致的烂叔,如果這些數(shù)據(jù)是為某一個開關服務的谨胞,想象一下重復的請求走進了不同的節(jié)點(在failover或者補償導致的場景下,重復請求是一定會發(fā)生的蒜鸡,也是服務化系統(tǒng)必須處理的)胯努,一個請求走了開關打開的邏輯,同時另外一個請求走了開關關閉的邏輯术瓮,這導致請求被處理兩次康聂,最壞的情況下會導致災難性的后果,就是資金損失胞四。
案例10:緩存數(shù)據(jù)結(jié)構(gòu)不一致
這個案例會時有發(fā)生恬汁,某系統(tǒng)需要種某一數(shù)據(jù)結(jié)構(gòu)的緩存,這一數(shù)據(jù)結(jié)構(gòu)有多個數(shù)據(jù)元素組成辜伟,其中氓侧,某個數(shù)據(jù)元素都需要從數(shù)據(jù)庫中或者服務中獲取,如果一部分數(shù)據(jù)元素獲取失敗导狡,由于程序處理不正確约巷,仍然將不完全的數(shù)據(jù)結(jié)構(gòu)存入緩存,那么緩存的消費者消費的時候很有可能因為沒有合理處理異常情況而出錯旱捧。
3 模式
3.1 生活中不一致問題的解決
大家回顧一下上一節(jié)列舉的生活中的案例1-買房独郎,如果置身事外來看,解決這種不一致的辦法有兩個枚赡,一個是避免不一致的發(fā)生氓癌,如果已經(jīng)是媳婦了就不好辦了:),還有一種方法就是慢慢的補償贫橙,先買個兩居贪婉,然后慢慢的等資金充裕了再換三居,買比特幣賺了再換帶花園的房子卢肃,于是問題最終被解決了疲迂,最終大家處于一致的狀態(tài)才顿,都開心了。這樣可以解決案例1的問題尤蒿,很自然由于有了過渡的方法郑气,問題在不經(jīng)意間就消失了,可見“過渡”也是解決一致性問題的一個模式优质。
從案例1的解決方案來看竣贪,我們要解決一致性問題,一個最直接最簡單的方法就是保持強一致性巩螃,對于案例1的情況演怎,盡量避免在結(jié)婚前兩個人能夠互相了解達成一致,避免不一致問題的發(fā)生避乏;不過有些事情事已至此爷耀,發(fā)生了就是發(fā)生了,出現(xiàn)了不一致的問題拍皮,我們應該考慮去補償歹叮,盡最大的努力從不一致狀態(tài)修復到一致狀態(tài),避免損失全部或者一部分铆帽,也不失為一個好方法咆耿。
因此,避免不一致是上策爹橱,出現(xiàn)了不一致及時發(fā)現(xiàn)及時修復是中策萨螺,有問題不積極解決留給他人解決是下策。
3.2 酸堿平衡理論
ACID在英文中的意思是“酸”愧驱,BASE的意識是“堿”慰技,這一段講的是“酸堿平衡”的故事。
1. ACID(酸)
如何保證強一致性呢组砚?計算機專業(yè)的童鞋在學習關系型數(shù)據(jù)庫的時候都學習了ACID原理吻商,這里對ACID做個簡單的介紹。如果想全面的學習ACID原理糟红,請參考ACID艾帐。
關系型數(shù)據(jù)庫天生就是解決具有復雜事務場景的問題,關系型數(shù)據(jù)庫完全滿足ACID的特性盆偿。
ACID指的是:
- A: Atomicity柒爸,原子性
- C: Consistency,一致性
- I: Isolation陈肛,隔離性
- D: Durability,持久性
具有ACID的特性的數(shù)據(jù)庫支持強一致性兄裂,強一致性代表數(shù)據(jù)庫本身不會出現(xiàn)不一致句旱,每個事務是原子的阳藻,或者成功或者失敗,事物間是隔離的,互相完全不影響,而且最終狀態(tài)是持久落盤的耕拷,因此吟逝,數(shù)據(jù)庫會從一個明確的狀態(tài)到另外一個明確的狀態(tài),中間的臨時狀態(tài)是不會出現(xiàn)的秘通,如果出現(xiàn)也會及時的自動的修復,因此是強一致的。
3個典型的關系型數(shù)據(jù)庫Oracle夹厌、Mysql、Db2都能保證強一致性裆悄,Oracle和Mysql使用多版本控制協(xié)議實現(xiàn)矛纹,而DB2使用改進的兩階段提交協(xié)議來實現(xiàn)。
如果你在為交易相關系統(tǒng)做技術(shù)選型光稼,交易的存儲應該只考慮關系型數(shù)據(jù)庫或南,對于核心系統(tǒng),如果需要較好的性能艾君,可以考慮使用更強悍的硬件采够,這種向上擴展(升級硬件)雖然成本較高,但是是最簡單粗暴有效的方式冰垄,另外蹬癌,Nosql完全不適合交易場景,Nosql主要用來做數(shù)據(jù)分析播演、ETL冀瓦、報表、數(shù)據(jù)挖掘写烤、推薦翼闽、日志處理等非交易場景。
前面提到的案例2-轉(zhuǎn)賬和案例3-下訂單和扣庫存都可以利用關系型數(shù)據(jù)庫的強一致性解決洲炊。
然而感局,前面提到,互聯(lián)網(wǎng)項目多數(shù)具有大規(guī)模高并發(fā)的特性暂衡,必須應用拆分的理念询微,對高并發(fā)的壓力采取“大而化小、小而化了”的方法狂巢,否則難以滿足動輒億級流量的需求撑毛,即使使用關系型數(shù)據(jù)庫,單機也難以滿足存儲和TPS上的需求唧领。為了保證案例2-轉(zhuǎn)賬可以利用關系型數(shù)據(jù)庫的強一致性藻雌,在拆分的時候盡量的把轉(zhuǎn)賬相關的賬戶放入一個數(shù)據(jù)庫分片雌续,對于案例3,盡量的保證把訂單和庫存放入同一個數(shù)據(jù)庫分片胯杭,這樣通過關系型數(shù)據(jù)庫自然就解決了不一致的問題驯杜。
然而,有些時候事與愿違做个,由于業(yè)務規(guī)則的限制鸽心,無法將相關的數(shù)據(jù)分到同一個數(shù)據(jù)庫分片,這個時候我們就需要實現(xiàn)最終一致性居暖。
對于案例2-轉(zhuǎn)賬場景顽频,假設賬戶數(shù)量巨大,對賬戶存儲進行了拆分膝但,關系型數(shù)據(jù)庫一共分了8個實例冲九,每個實例8個庫,每個庫8個表跟束,共512張表莺奸,假如要轉(zhuǎn)賬的兩個賬戶正好落在了一個庫里,那么可以依賴關系型數(shù)據(jù)庫的事務保持強一致性冀宴。
如果要轉(zhuǎn)賬的兩個賬戶正好落在了不同的庫里灭贷,轉(zhuǎn)賬操作是無法封裝在同一個數(shù)據(jù)庫事務中的,這個時候會發(fā)生一個庫的賬戶扣減余額成功略贮,另外一個庫的賬戶增加余額失敗的情況甚疟。
對于這種情況,我們需要繼續(xù)探討解決之道逃延,CAP原理和BASE原理览妖,BASE原理通過記錄事務的中間的臨時狀態(tài),實現(xiàn)最終一致性揽祥。
2. CAP(帽子理論)
如果想深入的學習CAP理論讽膏,請參考CAP。
由于對系統(tǒng)或者數(shù)據(jù)進行了拆分拄丰,我們的系統(tǒng)不再是單機系統(tǒng)府树,而是分布式系統(tǒng),針對分布式系的帽子理論包含三個元素:
- C:Consistency料按,一致性, 數(shù)據(jù)一致更新奄侠,所有數(shù)據(jù)變動都是同步的
- A:Availability,可用性, 好的響應性能载矿,完全的可用性指的是在任何故障模型下垄潮,服務都會在有限的時間處理響應
- P:Partition tolerance,分區(qū)容錯性,可靠性
帽子理論證明弯洗,任何分布式系統(tǒng)只可同時滿足二點甫题,沒法三者兼顧。關系型數(shù)據(jù)庫由于關系型數(shù)據(jù)庫是單節(jié)點的涂召,因此,不具有分區(qū)容錯性敏沉,但是具有一致性和可用性果正,而分布式的服務化系統(tǒng)都需要滿足分區(qū)容錯性,那么我們必須在一致性和可用性中進行權(quán)衡盟迟,具體表現(xiàn)在服務化系統(tǒng)處理的異常請求在某一個時間段內(nèi)可能是不完全的秋泳,但是經(jīng)過自動的或者手工的補償后,達到了最終的一致性攒菠。
3. BASE(堿)
BASE理論解決CAP理論提出了分布式系統(tǒng)的一致性和可用性不能兼得的問題迫皱,如果想全面的學習BASE原理,請參考Eventual consistency辖众。
BASE在英文中有“堿”的意思卓起,對應本節(jié)開頭的ACID在英文中“酸”的意思,基于這兩個名詞提出了酸堿平衡的結(jié)論凹炸,簡單來說是在不同的場景下戏阅,可以分別利用ACID和BASE來解決分布式服務化系統(tǒng)的一致性問題。
BASE模型與ACID模型截然不同啤它,滿足CAP理論奕筐,通過犧牲強一致性,獲得可用性变骡,一般應用在服務化系統(tǒng)的應用層或者大數(shù)據(jù)處理系統(tǒng)离赫,通過達到最終一致性來盡量滿足業(yè)務的絕大部分需求。
BASE模型包含個三個元素:
- BA:Basically Available塌碌,基本可用
- S:Soft State渊胸,軟狀態(tài),狀態(tài)可以有一段時間不同步
- E:Eventually Consistent誊爹,最終一致蹬刷,最終數(shù)據(jù)是一致的就可以了,而不是時時保持強一致
BASE模型的軟狀態(tài)是實現(xiàn)BASE理論的方法频丘,基本可用和最終一致是目標办成。按照BASE模型實現(xiàn)的系統(tǒng),由于不保證強一致性搂漠,系統(tǒng)在處理請求的過程中迂卢,可以存在短暫的不一致,在短暫的不一致窗口請求處理處在臨時狀態(tài)中,系統(tǒng)在做每步操作的時候而克,通過記錄每一個臨時狀態(tài)靶壮,在系統(tǒng)出現(xiàn)故障的時候,可以從這些中間狀態(tài)繼續(xù)未完成的請求處理或者退回到原始狀態(tài)员萍,最后達到一致的狀態(tài)腾降。
以案例1-轉(zhuǎn)賬為例,我們把用戶A給用戶B轉(zhuǎn)賬分成四個階段碎绎,第一個階段用戶A準備轉(zhuǎn)賬螃壤,第二個階段從用戶A賬戶扣減余額,第三個階段對用戶B增加余額筋帖,第四個階段完成轉(zhuǎn)賬奸晴。系統(tǒng)需要記錄操作過程中每一步驟的狀態(tài),一旦系統(tǒng)出現(xiàn)故障日麸,系統(tǒng)能夠自動發(fā)現(xiàn)沒有完成的任務寄啼,然后,根據(jù)任務所處的狀態(tài)代箭,繼續(xù)執(zhí)行任務墩划,最終完成任務,達到一致的最終狀態(tài)嗡综。
在實際應用中走诞,上面這個過程通常是通過持久化執(zhí)行任務的狀態(tài)和環(huán)境信息,一旦出現(xiàn)問題蛤高,定時任務會撈取未執(zhí)行完的任務蚣旱,繼續(xù)未執(zhí)行完的任務,直到執(zhí)行完成為止戴陡,或者取消已經(jīng)完成的部分操作回到原始狀態(tài)塞绿。這種方法在任務完成每個階段的時候,都要更新數(shù)據(jù)庫中任務的狀態(tài)恤批,這在大規(guī)模高并發(fā)系統(tǒng)中不會有太好的性能异吻,一個更好的辦法是用Write-Ahead Log(寫前日志),這和數(shù)據(jù)庫的Bin Log(操作日志)相似喜庞,在做每一個操作步驟诀浪,都先寫入日志,如果操作遇到問題而停止的時候延都,可以讀取日志按照步驟進行恢復雷猪,并且繼續(xù)執(zhí)行未完成的工作,最后達到一致晰房。寫前日志可以利用機械硬盤的追加寫而達到較好性能求摇,因此射沟,這是一種專業(yè)化的實現(xiàn)方式,多數(shù)業(yè)務系系統(tǒng)還是使用數(shù)據(jù)庫記錄的字段來記錄任務的執(zhí)行狀態(tài)与境,也就是記錄中間的“軟狀態(tài)”验夯,一個任務的狀態(tài)流轉(zhuǎn)一般可以通過數(shù)據(jù)庫的行級鎖來實現(xiàn),這比使用Write-Ahead Log實現(xiàn)更簡單摔刁、更快速挥转。
有了BASE理論作為基礎,我們對復雜的分布式事務進行拆解共屈,對其中的每一步驟都記錄其狀態(tài)扁位,有問題的時候可以根據(jù)記錄的狀態(tài)來繼續(xù)執(zhí)行任務,達到最終的一致趁俊,通過這個方法我們可以解決案例2-轉(zhuǎn)賬和案例3-下訂單和扣庫存中遇到的問題。
4. 酸堿平衡的總結(jié)
- 使用向上擴展(強悍的硬件)運行專業(yè)的關系型數(shù)據(jù)庫(例如:Oracle或者DB2)能夠保證強一致性刑然,錢能解決的問題就不是問題
- 如果錢是問題寺擂,可以對廉價硬件運行的開源關系型數(shù)據(jù)庫(例如:Mysql)進行分片,將相關的數(shù)據(jù)分到數(shù)據(jù)庫的同一個片泼掠,仍然能夠使用關系型數(shù)據(jù)庫保證事務
- 如果業(yè)務規(guī)則限制怔软,無法將相關的數(shù)據(jù)分到同一個片,就需要實現(xiàn)最終一致性择镇,通過記錄事務的軟狀態(tài)(中間狀態(tài)挡逼、臨時狀態(tài)),一旦處于不一致腻豌,可以通過系統(tǒng)自動化或者人工干預來修復不一致的情況
3.3 分布式一致性協(xié)議
國際開放標準組織Open Group定義了DTS(分布式事務處理模型)家坎,模型中包含4個角色:應用程序、事務管理器吝梅、資源管理器虱疏、通信資源管理器四部分。事務處理器是統(tǒng)管全局的管理者苏携,資源處理器和通信資源處理器是事務的參與者做瞪。
J2EE規(guī)范也包含此分布式事務處理模型的規(guī)范,并在所有的AppServer中進行實現(xiàn)右冻,J2EE規(guī)范中定義了TX協(xié)議和XA協(xié)議装蓬,TX協(xié)議定義應用程序與事務管理器之間的接口,而XA協(xié)議定義了事務管理器與資源處理器之間的接口纱扭,在過去牍帚,大家使用AppServer,例如:Websphere乳蛾、Weblogic履羞、Jboss等配置數(shù)據(jù)源的時候會看見類似XADatasource的數(shù)據(jù)源峦萎,這就是實現(xiàn)了DTS的關系型數(shù)據(jù)庫的數(shù)據(jù)源。企業(yè)級開發(fā)JEE中忆首,關系型數(shù)據(jù)庫爱榔、JMS服務扮演資源管理器的角色,而EJB容器則扮演事務管理器的角色糙及。
下面我們就介紹兩階段提交協(xié)議详幽、三階段提交協(xié)議以及阿里巴巴提出的TCC,它們都是根據(jù)DTS這一思想演變出來的浸锨。
1. 兩階段提交協(xié)議
上面描述的JEE的XA協(xié)議就是根據(jù)兩階段提交來保證事務的完整性唇聘,并實現(xiàn)分布式服務化的強一致性。
兩階段提交協(xié)議把分布式事務分成兩個過程柱搜,一個是準備階段迟郎,一個是提交階段,準備階段和提交階段都是由事務管理器發(fā)起的聪蘸,為了接下來講解方便宪肖,我們把事務管理器稱為協(xié)調(diào)者,把資管管理器稱為參與者健爬。
兩階段如下:
- 準備階段:協(xié)調(diào)者向參與者發(fā)起指令控乾,參與者評估自己的狀態(tài),如果參與者評估指令可以完成娜遵,參與者會寫redo或者undo日志(這也是前面提起的Write-Ahead Log的一種)蜕衡,然后鎖定資源,執(zhí)行操作设拟,但是并不提交
- 提交階段:如果每個參與者明確返回準備成功慨仿,也就是預留資源和執(zhí)行操作成功,協(xié)調(diào)者向參與者發(fā)起提交指令纳胧,參與者提交資源變更的事務镶骗,釋放鎖定的資源;如果任何一個參與者明確返回準備失敗躲雅,也就是預留資源或者執(zhí)行操作失敗鼎姊,協(xié)調(diào)者向參與者發(fā)起中止指令,參與者取消已經(jīng)變更的事務相赁,執(zhí)行undo日志相寇,釋放鎖定的資源
兩階段提交協(xié)議成功場景示意圖如下:
[圖片上傳失敗...(image-a5bbb8-1540468335419)]
我們看到兩階段提交協(xié)議在準備階段鎖定資源,是一個重量級的操作钮科,并能保證強一致性唤衫,但是實現(xiàn)起來復雜、成本較高绵脯,不夠靈活佳励,更重要的是它有如下致命的問題:
- 阻塞:從上面的描述來看休里,對于任何一次指令必須收到明確的響應,才會繼續(xù)做下一步赃承,否則處于阻塞狀態(tài)妙黍,占用的資源被一直鎖定,不會被釋放
- 單點故障:如果協(xié)調(diào)者宕機瞧剖,參與者沒有了協(xié)調(diào)者指揮拭嫁,會一直阻塞,盡管可以通過選舉新的協(xié)調(diào)者替代原有協(xié)調(diào)者抓于,但是如果之前協(xié)調(diào)者在發(fā)送一個提交指令后宕機做粤,而提交指令僅僅被一個參與者接受,并且參與者接收后也宕機捉撮,新上任的協(xié)調(diào)者無法處理這種情況
- 腦裂:協(xié)調(diào)者發(fā)送提交指令怕品,有的參與者接收到執(zhí)行了事務,有的參與者沒有接收到事務巾遭,就沒有執(zhí)行事務肉康,多個參與者之間是不一致的
上面所有的這些問題,都是需要人工干預處理恢总,沒有自動化的解決方案,因此兩階段提交協(xié)議在正常情況下能保證系統(tǒng)的強一致性睬愤,但是在出現(xiàn)異常情況下片仿,當前處理的操作處于錯誤狀態(tài),需要管理員人工干預解決尤辱,因此可用性不夠好砂豌,這也符合CAP協(xié)議的一致性和可用性不能兼得的原理。
2. 三階段提交協(xié)議
三階段提交協(xié)議是兩階段提交協(xié)議的改進版本光督。它通過超時機制解決了阻塞的問題阳距,并且把兩個階段增加為三個階段:
- 詢問階段:協(xié)調(diào)者詢問參與者是否可以完成指令,協(xié)調(diào)者只需要回答是還是不是结借,而不需要做真正的操作筐摘,這個階段超時導致中止
- 準備階段:如果在詢問階段所有的參與者都返回可以執(zhí)行操作,協(xié)調(diào)者向參與者發(fā)送預執(zhí)行請求船老,然后參與者寫redo和undo日志咖熟,執(zhí)行操作,但是不提交操作柳畔;如果在詢問階段任何參與者返回不能執(zhí)行操作的結(jié)果馍管,則協(xié)調(diào)者向參與者發(fā)送中止請求,這里的邏輯與兩階段提交協(xié)議的的準備階段是相似的薪韩,這個階段超時導致成功
- 提交階段:如果每個參與者在準備階段返回準備成功确沸,也就是預留資源和執(zhí)行操作成功捌锭,協(xié)調(diào)者向參與者發(fā)起提交指令,參與者提交資源變更的事務罗捎,釋放鎖定的資源观谦;如果任何一個參與者返回準備失敗,也就是預留資源或者執(zhí)行操作失敗宛逗,協(xié)調(diào)者向參與者發(fā)起中止指令坎匿,參與者取消已經(jīng)變更的事務,執(zhí)行undo日志雷激,釋放鎖定的資源替蔬,這里的邏輯與兩階段提交協(xié)議的提交階段一致
三階段提交協(xié)議成功場景示意圖如下:
[圖片上傳失敗...(image-ebbf7d-1540468335419)]
然而,這里與兩階段提交協(xié)議有兩個主要的不同:
- 增加了一個詢問階段屎暇,詢問階段可以確保盡可能早的發(fā)現(xiàn)無法執(zhí)行操作而需要中止的行為承桥,但是它并不能發(fā)現(xiàn)所有的這種行為,只會減少這種情況的發(fā)生
- 在準備階段以后根悼,協(xié)調(diào)者和參與者執(zhí)行的任務中都增加了超時凶异,一旦超時,協(xié)調(diào)者和參與者都繼續(xù)提交事務挤巡,默認為成功剩彬,這也是根據(jù)概率統(tǒng)計上超時后默認成功的正確性最大
三階段提交協(xié)議與兩階段提交協(xié)議相比,具有如上的優(yōu)點矿卑,但是一旦發(fā)生超時喉恋,系統(tǒng)仍然會發(fā)生不一致,只不過這種情況很少見罷了母廷,好處就是至少不會阻塞和永遠鎖定資源轻黑。
3. TCC
上面兩節(jié)講解了兩階段提交協(xié)議和三階段提交協(xié)議,實際上他們能解決案例2-轉(zhuǎn)賬和案例3-下訂單和扣庫存中的分布式事務的問題琴昆,但是遇到極端情況氓鄙,系統(tǒng)會發(fā)生阻塞或者不一致的問題,需要運營或者技術(shù)人工解決业舍。無論兩階段還是三階段方案中都包含多個參與者抖拦、多個階段實現(xiàn)一個事務,實現(xiàn)復雜舷暮,性能也是一個很大的問題蟋座,因此,在互聯(lián)網(wǎng)高并發(fā)系統(tǒng)中脚牍,鮮有使用兩階段提交和三階段提交協(xié)議的場景向臀。
阿里巴巴提出了新的TCC協(xié)議,TCC協(xié)議將一個任務拆分成Try诸狭、Confirm券膀、Cancel君纫,正常的流程會先執(zhí)行Try,如果執(zhí)行沒有問題芹彬,再執(zhí)行Confirm蓄髓,如果執(zhí)行過程中出了問題,則執(zhí)行操作的逆操Cancel舒帮,從正常的流程上講会喝,這仍然是一個兩階段的提交協(xié)議,但是玩郊,在執(zhí)行出現(xiàn)問題的時候肢执,有一定的自我修復能力,如果任何一個參與者出現(xiàn)了問題译红,協(xié)調(diào)者通過執(zhí)行操作的逆操作來取消之前的操作预茄,達到最終的一致狀態(tài)。
可以看出侦厚,從時序上耻陕,如果遇到極端情況下TCC會有很多問題的,例如刨沦,如果在Cancel的時候一些參與者收到指令诗宣,而一些參與者沒有收到指令,整個系統(tǒng)仍然是不一致的想诅,這種復雜的情況召庞,系統(tǒng)首先會通過補償?shù)姆绞剑瑖L試自動修復的裁眯,如果系統(tǒng)無法修復鹉梨,必須由人工參與解決。
從TCC的邏輯上看晌坤,可以說TCC是簡化版的三階段提交協(xié)議旦袋,解決了兩階段提交協(xié)議的阻塞問題,但是沒有解決極端情況下會出現(xiàn)不一致和腦裂的問題商乎。然而祭阀,TCC通過自動化補償手段鲜戒,會把需要人工處理的不一致情況降到到最少抹凳,也是一種非常有用的解決方案遏餐,根據(jù)線人,阿里在內(nèi)部的一些中間件上實現(xiàn)了TCC模式赢底。
我們給出一個使用TCC的實際案例失都,在秒殺的場景,用戶發(fā)起下單請求幸冻,應用層先查詢庫存粹庞,確認商品庫存還有余量,則鎖定庫存嘁扼,此時訂單狀態(tài)為待支付信粮,然后指引用戶去支付,由于某種原因用戶支付失敗趁啸,或者支付超時强缘,系統(tǒng)會自動將鎖定的庫存解鎖供其他用戶秒殺。
TCC協(xié)議使用場景示意圖如下:
[圖片上傳失敗...(image-1970e4-1540468335419)]
總結(jié)一下不傅,兩階段提交協(xié)議旅掂、三階段提交協(xié)議、TCC協(xié)議都能保證分布式事務的一致性访娶,他們保證的分布式系統(tǒng)的一致性從強到弱商虐,TCC達到的目標是最終一致性,其中任何一種方法都可以不同程度的解決案例2:轉(zhuǎn)賬崖疤、案例3:下訂單和扣庫存的問題秘车,只是實現(xiàn)的一致性的級別不一樣而已,對于案例4:同步超時可以通過TCC的理念解決劫哼,如果同步調(diào)用超時叮趴,調(diào)用方可以使用fastfail策略,返回調(diào)用方的使用方失敗的結(jié)果权烧,同時調(diào)用服務的逆向cancel操作,保證服務的最終一致性妻率。
3.4 保證最終一致性的模式
在大規(guī)模高并發(fā)服務化系統(tǒng)中宫静,一個功能被拆分成多個具有單一功能的元功能温技,一個流程會有多個系統(tǒng)的多個元功能組合實現(xiàn)舵鳞,如果使用兩階段提交協(xié)議和三階段提交協(xié)議,確實能解決系統(tǒng)間一致性問題套才,除了這兩個協(xié)議帶來的自身的問題背伴,這些協(xié)議的實現(xiàn)比較復雜傻寂、成本比較高,最重要的是性能并不好静檬,相比來看拂檩,TCC協(xié)議更簡單、容易實現(xiàn)钉迷,但是TCC協(xié)議由于每個事務都需要執(zhí)行Try,再執(zhí)行Confirm谐鼎,略微顯得臃腫狸棍,因此草戈,在現(xiàn)實的系統(tǒng)中丙猬,底線要求僅僅需要能達到最終一致性茧球,而不需要實現(xiàn)專業(yè)的、復雜的一致性協(xié)議揪垄,實現(xiàn)最終一致性有一些非常有效的福侈、簡單粗暴的模式肪凛,下面就介紹這些模式及其應用場景。
1. 查詢模式
任何一個服務操作都需要提供一個查詢接口戳葵,用來向外部輸出操作執(zhí)行的狀態(tài)拱烁。服務操作的使用方可以通過查詢接口,得知服務操作執(zhí)行的狀態(tài),然后根據(jù)不同狀態(tài)來做不同的處理操作猛们。
為了能夠?qū)崿F(xiàn)查詢弯淘,每個服務操作都需要有唯一的流水號標識惯吕,也可使用此次服務操作對應的資源ID來標志废登,例如:請求流水號堡距、訂單號等。
首先易稠,單筆查詢操作是必須提供的驶社,我們也鼓勵使用單筆訂單查詢,這是因為每次調(diào)用需要占用的負載是可控的份乒,批量查詢則根據(jù)需要來提供或辖,如果使用了批量查詢,需要有合理的分頁機制蟀架,并且必須限制分頁的大小片拍,以及對批量查詢的QPS需要有容量評估和流控等捌省。
查詢模式的示意圖如下:
[圖片上傳失敗...(image-41701d-1540468335419)]
對于案例4:同步超時、案例5:異步回調(diào)超時祝高、案例6:掉單工闺、案例7:系統(tǒng)間狀態(tài)不一致,我們都需要使用查詢模式來了解被調(diào)用服務的處理情況叠殷,來決定下一步做什么:補償未完成的操作還是回滾已經(jīng)完成的操作林束。
2. 補償模式
有了上面的查詢模式,在任何情況下依痊,我們都能得知具體的操作所處的狀態(tài)胸嘁,如果整個操作處于不正常的狀態(tài),我們需要修正操作中有問題的子操作毫胜,這可能需要重新執(zhí)行未完成的子操作酵使,后者取消已經(jīng)完成的子操作口渔,通過修復使整個分布式系統(tǒng)達到一致痪欲,為了讓系統(tǒng)最終一致而做的努力都叫做補償业踢。
對于服務化系統(tǒng)中同步調(diào)用的操作陨亡,業(yè)務操作發(fā)起的主動方在還沒有得到業(yè)務操作執(zhí)行方的明確返回或者調(diào)用超時负蠕,場景可參考案例4:同步超時,這個時候業(yè)務發(fā)起的主動方需要及時的調(diào)用業(yè)務執(zhí)行方獲得操作執(zhí)行的狀態(tài)欲账,這里使用查詢模式赛不,獲得業(yè)務操作的執(zhí)行方的狀態(tài)后踢故,如果業(yè)務執(zhí)行方已經(jīng)完預設的工作殿较,則業(yè)務發(fā)起方給業(yè)務的使用方返回成功,如果業(yè)務操作的執(zhí)行方的狀態(tài)為失敗或者未知洽瞬,則會立即告訴業(yè)務的使用方失敗伙窃,然后調(diào)用業(yè)務操作的逆向操作位他,保證操作不被執(zhí)行或者回滾已經(jīng)執(zhí)行的操作,讓業(yè)務的使用方京景、業(yè)務發(fā)起的主動方确徙、業(yè)務的操作方最終達成一致的狀態(tài)。
補償模式的示意圖如下:
[圖片上傳失敗...(image-48791e-1540468335419)]
補償操作根據(jù)發(fā)起形式分為:
- 自動恢復:程序根據(jù)發(fā)生不一致的環(huán)境伴逸,通過繼續(xù)未完成的操作错蝴,或者回滾已經(jīng)完成的操作顷锰,自動來達到一致
- 通知運營:如果程序無法自動恢復,并且設計時考慮到了不一致的場景万矾,可以提供運營功能良狈,通過運營手工進行補償
- 通知技術(shù):如果很不巧薪丁,系統(tǒng)無法自動回復粱檀,又沒有運營功能茄蚯,那必須通過技術(shù)手段來解決,技術(shù)手段包括走數(shù)據(jù)庫變更或者代碼變更來解決皱碘,這是最糟的一種場景
3. 異步確保模式
異步確保模式是補償模式的一個典型案例癌椿,經(jīng)常應用到使用方對響應時間要求并不太高,我們通常把這類操作從主流程中摘除都办,通過異步的方式進行處理,處理后把結(jié)果通過通知系統(tǒng)通知給使用方槽卫,這個方案最大的好處能夠?qū)Ω卟l(fā)流量進行消峰,例如:電商系統(tǒng)中的物流躲庄、配送噪窘,以及支付系統(tǒng)中的計費直砂、入賬等静暂。
實踐中,將要執(zhí)行的異步操作封裝后持久入庫,然后通過定時撈取未完成的任務進行補償操作來實現(xiàn)異步確保模式听绳,只要定時系統(tǒng)足夠健壯,任何一個任務最終會被成功執(zhí)行鼠证。
異步確保模式的示意圖如下:
[圖片上傳失敗...(image-8c61e7-1540468335419)]
對于案例5:異步回調(diào)超時靠抑,使用的就是異步確保模式量九,這種情況下對于某個操作,如果遲遲沒有收到響應颂碧,我們通過查詢模式和補償模式來繼續(xù)未完成的操作荠列。
4. 定期校對模式
既然我們在系統(tǒng)中實現(xiàn)最終一致性,系統(tǒng)在沒有達到一致之前载城,系統(tǒng)間的狀態(tài)是不一致的肌似,甚至是混亂的,需要補償操作來達到一致的目的川队,但是我們?nèi)绾蝸戆l(fā)現(xiàn)需要補償?shù)牟僮髂兀?/p>
在操作的主流程中的系統(tǒng)間執(zhí)行校對操作昔脯,我們可以事后異步的批量校對操作的狀態(tài),如果發(fā)現(xiàn)不一致的操作,則進行補償猬膨,補償操作與補償模式中的補償操作是一致的。
另外,實現(xiàn)定期校對的一個關鍵就是分布式系統(tǒng)中需要有一個自始至終唯一的ID饼丘,ID的生成請參考SnowFlake村砂。
在分布式系統(tǒng)中帘瞭,全局唯一ID的示意圖如下:
[圖片上傳失敗...(image-8a2f09-1540468335419)]
一般情況下担敌,生成全局唯一ID有兩種方法:
- 持久型:使用數(shù)據(jù)庫表自增字段或者Sequence生成,為了提高效率,每個應用節(jié)點可以緩存一批次的ID躯砰,如果機器重啟可能會損失一部分ID,但是這并不會產(chǎn)生任何問題
- 時間型:一般由機器號味榛、業(yè)務號、時間鞠苟、單節(jié)點內(nèi)自增ID組成冀惭,由于時間一般精確到秒或者毫秒,因此不需要持久就能保證在分布式系統(tǒng)中全局唯一宜狐、粗略遞增能特點
實踐中逸嘀,為了能在分布式系統(tǒng)中迅速的定位問題售躁,一般的分布式系統(tǒng)都有技術(shù)支持系統(tǒng)驰怎,它能夠跟蹤一個請求的調(diào)用鏈精肃,調(diào)用鏈是在二維的維度跟蹤一個調(diào)用請求肉拓,最后形成一個調(diào)用樹迟几,原理可參考谷歌的論文Dapper, a Large-Scale Distributed Systems Tracing Infrastructure贺辰,一個開源的參考實現(xiàn)為pinpoint。
在分布式系統(tǒng)中廊敌,調(diào)用鏈的示意圖如下:
[圖片上傳失敗...(image-e130a7-1540468335419)]
全局的唯一流水ID可以把一個請求在分布式系統(tǒng)中的流轉(zhuǎn)的路徑聚合,而調(diào)用鏈中的spanid可以把聚合的請求路徑通過樹形結(jié)構(gòu)進行展示减拭,讓技術(shù)支持人員輕松的發(fā)現(xiàn)系統(tǒng)出現(xiàn)的問題,能夠快速定位出現(xiàn)問題的服務節(jié)點,提高應急效率签孔。
關于訂單跟蹤昌执、調(diào)用鏈跟蹤、業(yè)務鏈跟蹤诈泼,我們會在后續(xù)文章中詳細介紹懂拾。
在分布式系統(tǒng)中構(gòu)建了唯一ID,調(diào)用鏈等基礎設施铐达,我們很容易對系統(tǒng)間的不一致進行核對岖赋,通常我們需要構(gòu)建第三方的定期核對系統(tǒng),以第三方的角度來監(jiān)控服務執(zhí)行的健康程度瓮孙。
定期核對系統(tǒng)示意圖如下:
[圖片上傳失敗...(image-55ff67-1540468335419)]
對于案例6:掉單唐断、案例7:系統(tǒng)間狀態(tài)不一致通常通過定期校對模式發(fā)現(xiàn)問題选脊,并通過補償模式來修復,最后完成系統(tǒng)間的最終一致性脸甘。
定期校對模式多應用在金融系統(tǒng)恳啥,金融系統(tǒng)由于涉及到資金安全,需要保證百分之百的準確性丹诀,所以钝的,需要多重的一致性保證機制,包括:系統(tǒng)間的一致性對賬铆遭、現(xiàn)金對賬硝桩、賬務對賬、手續(xù)費對賬等等疚脐,這些都屬于定期校對模式亿柑,順便說一下,金融系統(tǒng)與社交應用在技術(shù)上本質(zhì)的區(qū)別在于社交應用在于量大棍弄,而金融系統(tǒng)在于數(shù)據(jù)的準確性望薄。
到現(xiàn)在為止,我們看到通過查詢模式呼畸、補償模式痕支、定期核對模式可以解決案例4到案例7的所有問題,對于案例4:同步超時蛮原,如果同步超時卧须,我們需要查詢狀態(tài)進行補償,對于案例5:異步回調(diào)超時儒陨,如果遲遲沒有收到回調(diào)響應花嘶,我們也會通過查詢狀態(tài)進行補償,對于案例6:掉單蹦漠、案例7:系統(tǒng)間狀態(tài)不一致椭员,我們通過定期核對模式可以保證系統(tǒng)間操作的一致性,避免掉單和狀態(tài)不一致導致問題笛园。
5. 可靠消息模式
在分布式系統(tǒng)中隘击,對于主流程中優(yōu)先級比較低的操作,大多采用異步的方式執(zhí)行研铆,也就是前面提到的異步確保型埋同,為了讓異步操作的調(diào)用方和被調(diào)用方充分的解耦,也由于專業(yè)的消息隊列本身具有可伸縮棵红、可分片凶赁、可持久等功能,我們通常通過消息隊列實現(xiàn)異步化,對于消息隊列哟冬,我們需要建立特殊的設施保證可靠的消息發(fā)送以及處理機的冪等等楼熄。
消息的可靠發(fā)送
消息的可靠發(fā)送可以認為是盡最大努力發(fā)送消息通知忆绰,有兩種實現(xiàn)方法:
第一種浩峡,發(fā)送消息之前,把消息持久到數(shù)據(jù)庫错敢,狀態(tài)標記為待發(fā)送翰灾,然后發(fā)送消息,如果發(fā)送成功稚茅,將消息改為發(fā)送成功纸淮。定時任務定時從數(shù)據(jù)庫撈取一定時間內(nèi)未發(fā)送的消息,將消息發(fā)送亚享。
[圖片上傳失敗...(image-539ce-1540468335419)]
第二種咽块,實現(xiàn)方式與第一種類似,不同的是持久消息的數(shù)據(jù)庫是獨立的欺税,并不耦合在業(yè)務系統(tǒng)中侈沪。發(fā)送消息之前,先發(fā)送一個預消息給某一個第三方的消息管理器晚凿,消息管理器將其持久到數(shù)據(jù)庫亭罪,并標記狀態(tài)為待發(fā)送,發(fā)送成功后歼秽,標記消息為發(fā)送成功应役。定時任務定時從數(shù)據(jù)庫撈取一定時間內(nèi)未發(fā)送的消息,回查業(yè)務系統(tǒng)是否要繼續(xù)發(fā)送燥筷,根據(jù)查詢結(jié)果來確定消息的狀態(tài)箩祥。
[圖片上傳失敗...(image-7d987-1540468335419)]
一些公司把消息的可靠發(fā)送實現(xiàn)在了中間件里,通過Spring的注入肆氓,在消息發(fā)送的時候自動持久消息記錄袍祖,如果有消息記錄沒有發(fā)送成功,定時會補償發(fā)送做院。
消息處理器的冪等性
如果我們要保證消息可靠的發(fā)送盲泛,簡單來說,要保證消息一定要發(fā)送出去键耕,那么就需要有重試機制寺滚,有了重試機制,消息一定會重復屈雄,那么我們需要對重復做處理村视。
處理重復的最佳方式為保證操作的冪等性,冪等性的數(shù)學公式為:
f(f(x)) = f(x)
保證操作的冪等性常用的幾個方法:
- 使用數(shù)據(jù)庫表的唯一鍵進行濾重酒奶,拒絕重復的請求
- 使用分布式表對請求進行濾重
- 使用狀態(tài)流轉(zhuǎn)的方向性來濾重蚁孔,通常使用行級鎖來實現(xiàn)(后續(xù)在鎖相關的文章中詳細說明)
- 根據(jù)業(yè)務的特點奶赔,操作本身就是冪等的,例如:刪除一個資源杠氢、增加一個資源站刑、獲得一個資源等
6. 緩存一致性模型
大規(guī)模高并發(fā)系統(tǒng)中一個常見的核心需求就是億級的讀需求,顯然鼻百,關系型數(shù)據(jù)庫并不是解決高并發(fā)讀需求的最佳方案绞旅,互聯(lián)網(wǎng)的經(jīng)典做法就是使用緩存抗讀需求,下面有一些使用緩存的保證一致性的最佳實踐:
- 如果性能要求不是非常的高温艇,盡量使用分布式緩存因悲,而不要使用本地緩存
- 種緩存的時候一定種完全,如果緩存數(shù)據(jù)的一部分有效勺爱,一部分無效晃琳,寧可放棄種緩存,也不要把部分數(shù)據(jù)種入緩存
- 數(shù)據(jù)庫與緩存只需要保持弱一致性琐鲁,而不需要強一致性卫旱,讀的順序要先緩存,后數(shù)據(jù)庫绣否,寫的順序要先數(shù)據(jù)庫誊涯,后緩存
這里的最佳實踐能夠解決案例8:緩存和數(shù)據(jù)庫不一致、案例9:本地緩存節(jié)點間不一致蒜撮、案例10:緩存數(shù)據(jù)結(jié)構(gòu)不一致的問題暴构,對于數(shù)據(jù)存儲層、緩存與數(shù)據(jù)庫段磨、Nosql等的一致性是更深入的存儲一致性技術(shù)取逾,將會在后續(xù)文章單獨介紹,這里的數(shù)據(jù)一致性主要是處理應用層與緩存苹支、應用層與數(shù)據(jù)庫砾隅、一部分的緩存與數(shù)據(jù)庫的一致性。
3.5 專題模式
這一節(jié)介紹特殊場景下的一致性問題和解決方案债蜜。
遷移開關的設計
在大多數(shù)企業(yè)里晴埂,新項目和老項目一般會共存,大家都在努力的下掉老項目寻定,但是由于種種原因總是下不掉儒洛,如果要徹底的下掉老項目,就必須要有非常完善的遷移方案狼速,遷移是一項非常復雜而艱巨的任務琅锻,我會在將來的文章中詳細探討遷移方案、流程和技術(shù),這里我們只對遷移中使用的開關進行描述恼蓬。
遷移過程必須使用開關惊完,開關一般都會基于多個維度來設計,例如:全局的处硬、用戶的小槐、角色的、商戶的郁油、產(chǎn)品的等等本股,如果遷移過程中遇到問題,我們需要關閉開關桐腌,遷移回老的系統(tǒng),這需要我們的新系統(tǒng)兼容老的數(shù)據(jù)苟径,老的系統(tǒng)也兼容新的數(shù)據(jù)案站,從某種意義上來講,遷移比實現(xiàn)新系統(tǒng)更加困難棘街。
曾經(jīng)看過很多簡單的開關設計蟆盐,有的開關設計在應用層次,通過一個curl語句調(diào)用遭殉,沒有權(quán)限控制石挂,這樣的開關在服務池的每個節(jié)點都是不同步的、不一致的险污;還有的系統(tǒng)把開關配置放在中心化的配置系統(tǒng)痹愚、數(shù)據(jù)庫或者緩存等,處理的每個請求都通過統(tǒng)一的開關來判斷是否遷移等等蛔糯,這樣的開關有一個致命的缺點拯腮,服務請求在處理過程中,開關可能會變化蚁飒,各個節(jié)點之間開關可能不同步动壤、不一致,導致重復的請求可能走到新的邏輯又走了老的邏輯淮逻,如果新的邏輯和老的邏輯沒有保證冪等性琼懊,這個請求就被重復處理了,如果是金融行業(yè)的應用爬早,可能會導致資金損失哼丈,電商系統(tǒng)可能會導致發(fā)貨并退款等問題。
這里面我們推薦使用訂單開關凸椿,不管我們在什么維度上設計了開關削祈,接收到服務請求后,我們在請求創(chuàng)建的關聯(lián)實體(例如:訂單)上標記開關,以后的任何處理流程髓抑,包括同步的和異步的處理流程咙崎,都通過訂單上的開關來判斷,而不是通過全局的或者基于配置的開關吨拍,這樣在訂單創(chuàng)建的時候褪猛,開關已經(jīng)確定,不再變更羹饰,一旦一份數(shù)據(jù)不再發(fā)生變化伊滋,那么它永遠是線程安全的,并且不會有不一致的問題队秩。
這個模式在生產(chǎn)中使用比較頻繁笑旺,建議每個企業(yè)都把這個模式作為設計評審的一項,如果不檢查這一項馍资,很多開發(fā)童鞋都會偷懶筒主,直接在配置中或者數(shù)據(jù)庫中做個開關就上線了。
4 總結(jié)
本文從一致性問題的實踐出發(fā)鸟蟹,從大規(guī)模高并發(fā)服務化系統(tǒng)的實踐經(jīng)驗中進行總結(jié)乌妙,列舉導致不一致的具體問題,圍繞著具體問題建钥,總結(jié)出解決不一致的方法藤韵,并且抽象成模式,供大家在開發(fā)服務化系統(tǒng)的過程中參考熊经。
另外泽艘,由于篇幅有限,還有一些關于分布式一致性的技術(shù)無法在一篇文章中與大家分享奈搜,包括:paxos算法悉盆、raft算法、zab算法馋吗、nwr算法焕盟、一致性哈希等,我會在后續(xù)文章中詳細介紹宏粤。
作者:李艷鵬
鏈接:http://www.reibang.com/p/1156151e20c8
來源:簡書
簡書著作權(quán)歸作者所有脚翘,任何形式的轉(zhuǎn)載都請聯(lián)系作者獲得授權(quán)并注明出處。