「什麼是分佈式系統?這取決於看系統的角度。對於坐在鍵盤前使用IBM我的電腦的人來講,電腦不是一個分佈式的系統。但對於在電腦主板上趴着的蟲子來講,這臺電腦就是一個分佈式系統。」 —— Leslie Lamport程序員
引言算法
分佈式一致性問題隨處可見,任何一個實體/聯接模型,均可能存在分佈式一致性問題。若是把單機拆開來看,CPU、內存、I/O設備組成的機箱自己就是一個小型的分佈式系統,須要確保對這個系統操做的最終一致性。幸運的是這部分工做已經交給操做系統和數據庫軟件來幫咱們完成。而在大型分佈式企業級應用中,分佈式最終一致性方案須要根據系統自身特色量身定製,是系統設計的重點。近年來隨着滬江業務的快速增加和微服務治理推廣,本地ACID事務早已不能知足業務和系統的發展需求。大部分業務流程都須要跨多微服務的調用來協做完成,而且要求系統確保分佈式最終一致性。數據庫
能夠選擇分佈式事務框架方案,目前主流的分佈式事務框架大體可分爲3類實現 :後端
基於XA協議的兩階段提交(2PC)方案微信
基於支付寶最先提出的TCC(Try、Confirm、Cancel)方案網絡
基於ebay最先提出的消息隊列異步確保方案架構
此外還有較輕的解決方案,業務系統能夠根據自身須要,選擇經過冪等/重試、狀態機、恢復日誌、異步校驗等技術來確保最終一致性。併發
重型武器框架
採用分佈式事務框架的方案,最終一致性由分佈式事務框架保證,業務程序員對框架細節徹底透明。選擇這種方案,須要注意幾個點。首先,因爲分階段提交協議自己的脆弱性,主流分階段提交協議如2PC,3PC, TCC都沒法徹底確保最終一致性,要採用異步校驗的手段兜底。其次,分階段提交協議帶來的高延遲,屢次協議通訊RTT帶來的時間損耗。第三,基於消息隊列異步確保的分佈式事務框架實現,須要考慮消息可靠性和業務侵入問題。分佈式事務框架也有巨大的優點,首先,分佈式事務被框架封裝成切面,業務開發只需關心純業務。其次,分佈式事務的代碼開發量大大減小。對一致性和代碼質量有極高要求的銀行、金融領域,分佈式事務框架是最佳選擇。異步
輕型武器
不一樣於採用分佈式事務框架的最終一致性方案,程序員也能夠選擇經過冪等/重試、狀態機、恢復日誌、異步校驗等技術來確保最終一致性。這種方案不受限於平臺和框架,系統較精簡靈活,初期業務系統大都基於這種分佈式一致性解決方案。不過這種方案對業務開發的要求更高,分佈式一致性邏輯要業務程序員代碼實現,容易出現bug。
其實,主流的分佈式事務框架也是經過這些基本的系統機制如冪等/重試、狀態機、恢復日誌、異步校驗等來確保的最終一致性,對比兩種方案,下文主要圍繞後一種展開論述,討論5點使系統達成分佈式最終一致性的技術實踐。
原則
一、CAP定理
以下三屬性,任何一個聯網的共享數據系統最多隻能同時知足 2 個 :
一致性(Consistency) : 每次讀取都會收到最新的寫入或錯誤
可用性(Availability) : 每一個請求都會收到一個不是錯誤的響應
分區容忍性(Partition tolerance) : 節點之間的網絡丟棄(或延遲)了任意數量的消息,系統仍繼續運行
因爲分區容忍性是可伸縮的最基本要求,放棄分區容忍性等於放棄可伸縮,因此分區容忍性是必選項,大部分的分佈式系統都是在C和A之間作選擇。須要注意的是,雖然C都叫一致性,但CAP定理中的C和數據庫事務ACID中的C是徹底不一樣的兩個定義。
二、BASE原則
Basically Available : 基本可用
Soft state : 軟狀態
Eventual consistency : 最終一致性
BASE原則發展自CAP定理,捨棄了系統的強一致性而選擇AP,但每一個應用能夠根據自身的業務特色,採用適當的方式來使系統達到最終一致性。用較通俗的話來描述就是 : 「過程寬鬆,結果嚴格,你的老闆不關心過程,只看結果」。NoSQL數據庫Cassandra就是遵循的BASE原則設計。不過也有分佈式系統設計不是遵循BASE原則,而是選擇CAP中的CP,如HBase。固然,系統對CAP三者的取捨並非一成不變,能夠根據實際須要改變策略。
實踐
一、重試
重試機制可使分佈式不一致數據自動恢復,前提是重試接口要提供冪等保證。重試機制是達成分佈式最終一致性的重要手段。例如,超時重傳是TCP協議保證數據可靠性的一個重要機制,核心思想其實就是重試。在此我向你們推薦一個架構學習交流裙。交流學習裙號:687810532,裏面會分享一些資深架構師錄製的視頻錄像
同步重試 : 在上次請求失敗或超時,程序再次發起同步調用請求。後端程序不推薦同步重試,其一由於同步等待佔用系統線程資源,其二由於重試引發的流量放大,可能致使系統雪崩。
異步重試 : 經過異步系統(消息隊列或調度中間件)對失敗或超時請求再次發起調用。推薦這種方式的重試,重試的時間間隔能夠設置爲根據重試次數指數增加,超太重試閾值仍未成功,能夠報警通知並由人工訂正。
重試也是提升系統可用性的一種有效手段。若是一個服務的可用性爲98%(有1個9),1次重試以後其可用性可達到99.96%(3個9),2次重試能夠達到99.9992%(5個9)。
二、冪等
冪等的數學定義爲
用通俗的話來講就是 : 相同的操做執行屢次 和 執行一次產生的效果是同樣的。有的操做是自然冪等的,如查詢、刪除操做。有的操做是人爲使其冪等,例如TCP的超時重傳操做就是冪等的,不管客戶端將一個seq字節傳送多少次,服務端窗口只會用一次該字節。冪等實現方式有不少 :
基於記錄的悲觀鎖,MySQL中經過SELECT FOR UPDATE語句實現。這種實現方式要設置AUTOCOMMIT=0,加鎖和更新記錄在同一個事務中,長時間鎖定記錄會下降系統的TPS,高併發場景不推薦使用。
基於記錄版本號或狀態機的樂觀鎖方案,適用於更新數據場景。例如,用戶下單購買一個商品的扣庫存操做實現冪等,能夠用以下SQL語句實現 : UPDATE stocktable SET stock = stock - 1, version = version + 1 WHERE product_id = 123 and version = 1
基於數據庫惟一索引的去重表,適用於插入和更新數據的場景,由數據庫唯一索引確保屢次插入和更新操做只有一次生效。
基於全局惟一標識token實現,這種方案要注意幾點 : 一、這裏校驗token是否可以使用 和 置token爲已使用,是一個CAS原子操做,須要確保在一個原子操做中。 二、若是token存儲使用的是Redis,那麼驗證token的CAS操做可使用原子自增操做incr,若是Redis值大於1則token不可以使用,反之可以使用。還有一種實現方式是token生成系統將token預先寫入Redis,用刪除操做來校驗token是否被使用,刪除成功表明token未被使用可執行操做。 三、若是token存儲使用的是MySQL,根據token分庫分表和建唯一索引,同時經過insert語句來判斷token是否存在,若是insert失敗則token不可以使用,反之可以使用。
三、狀態機
狀態機是表示實體的狀態根據條件轉移的數學模型。經過狀態機模型,系統能夠判斷當前不一致狀態,以及如何校訂不一致狀態到一致狀態。這樣說可能比較抽象,咱們拿發微信羣紅包的例子來講明。當你點開發紅包按鈕,輸入總金額、紅包個數、標題,點擊支付成功後。其實根據時間前後紅包系統後臺至少經歷過這樣一個狀態機 :
一、當輸入總金額、紅包個數、標題點擊提交,首前後臺建立一個初始化狀態(INIT)紅包
二、接着系統將根據你輸入的總金額和個數n將紅包拆分紅n分,此時紅包的狀態爲拆分紅功(SPLITTED)
三、此時紅包後臺會監聽異步支付消息,若是支付成功則將紅包置爲支付成功(PAID)
四、以後紅包系統會通知微信IM系統,發送消息通知羣裏的用戶,此時紅包狀態爲(NOTIFIED)
5.一、羣裏的用戶把紅包搶光了,紅包狀態被系統置爲已搶光(RUNOUT)
5.二、還有一種可能,若是羣裏都是程序員,忙着擼代碼,沒時間搶紅包,必定時間後紅包自動退款到支付帳號,紅包狀態便爲(REFUND)
這只是一個正常業務流程的紅包狀態機,異常狀況如拆分失敗、支付失敗、通知失敗、退款失敗等狀況也同理存在一個狀態機器。爲了方便業務實體狀態回滾和校訂,狀態機要儘可能設計精簡,轉移到下一個狀態的邊儘量的只有一條路徑(終結狀態會例外),這樣在回滾和校訂時可以明確前一個狀態和後一個狀態。舉個例子,若是系統發現紅包一直處於PAID狀態,而並無流轉到NOTIFIED狀態,可以判斷是通知羣用戶出現異常,能夠根據實際狀況從新通知羣用戶或者將超期紅包退款。
四、恢復日誌
恢復日誌是程序現場的記錄,也是業務數據恢復的重要依據。恢復日誌log要求全局惟一的requestId來標示請求(實際的業務場景可採用不會重複有含義的業務id),出現異常,能夠根據requestId維度redo和undo業務操做,恢復日誌具體可分爲三部分 :
requestId請求開始時,記錄REQUEST START requestId
本地修改時,記錄所有的(requestId,x,originalValue, destValue)四元組,x表明操做對象,修改前x的值爲originalValue,本次修改的目的操做值爲destValue
requestId結束時,記錄REQUEST End requestId
恢復日誌是系統從不一致的狀態恢復到一致狀態的重要數據,丟失恢復日誌,意味着不一致可能沒法恢復。爲何是可能,由於有時能夠經過狀態機對不一致的狀態進行恢復。
五、異步校驗
完全解決分佈式一致性問題,有著名的Paxos算法,經過該算法分佈式系統自發達成一致性。而在具體的業務場景,徹底不須要系統自發的達成共識,咱們只要在業務系統外部加上嚴格的業務約束,用來仲裁業務系統的狀態。經過異步校驗,能夠發現分佈式系統中的異常狀態,並經過恢復日誌進行腳本批量恢復或者人工處理恢復,根據校驗的粒度有 :
根據業務實體id校驗,使用消息隊列,將須要校驗業務id投遞給校驗系統,進行異步校驗。
根據時間維度批量校驗,使用異步調度框架,根據時間粒度批量獲取進行異步校驗。
此外,並非全部系統都有可靠消息隊列和調度服務支撐,業務系統能夠增長一個本地業務id校驗回執字段,校驗系統根據校驗步驟回調設置校驗回執字段,並對校驗未經過的數據進行重校驗或者訂正。
總結
分佈式最終一致性問題,後端程序員在實際開發中常常遇到。在實際系統開發中爲了確保最終一致性,每每須要組合多個技術點打出組合拳,由於招數是死的,程序員是活的。總結上面提到的技術點,咱們能夠經過冪等和重試機制,使得不一致數據可以自動恢復;經過異步校驗機制發現業務系統的不一致數據;經過狀態機和恢復日誌,糾正不一致的業務數據。最後,感謝閱讀本文,歡迎留言討論。在此我向你們推薦一個架構學習交流裙。交流學習裙號:687810532,裏面會分享一些資深架構師錄製的視頻錄像