分佈式系統最終一致性

前言

目前的應用系統,不管是企業級應用還是互聯網應用,最終數據的一致性是每個應用系統都要面臨的問題,隨着分佈式的逐漸普及,數據一致性更加艱難,但是也很難有銀彈的解決方案,也並不是引入特定的中間件或者特定的開源框架能夠解決的,更多的還是看業務場景,根據場景來給出解決方案。根據筆者最近幾年的瞭解,總結了幾個點,更多的應用系統在編碼的時候,更加關注數據的一致性,這樣系統纔是健壯的。

基礎理論相關

說起事務,目前的幾個理論,ACID事務特性,CAP分佈式理論,以及BASE等,ACID在數據庫事務中體現,CAP和BASE則是分佈式事務的理論,結合業務系統,例如訂單管理,例如倉儲管理等,可以借鑑這些理論,從而解決問題。

  • ACID 特性

    • A(原子性)事務的原子操作單元,對數據的修改,要麼全部執行,要麼全部不執行;

    • C(一致性)在事務開始和完成時,數據必須保持一致狀態,相關的數據規則必須應用於事務的修改,以保證數據的完整性,事務結束時,所有的內部數據結構必須正確;

    • I(隔離性)保證事務不受外部併發操作的獨立環境執行;

    • D(持久性)事務完成之後,對於數據的修改是永久的,即使系統出現故障也能夠保持;

  • CAP

    • C(一致性)一致性是指數據的原子性,在經典的數據庫中通過事務來保障,事務完成時,無論成功或回滾,數據都會處於一致的狀態,在分佈式環境下,一致性是指多個節點數據是否一致;

    • A(可用性)服務一直保持可用的狀態,當用戶發出一個請求,服務能在一定的時間內返回結果;

    • P(分區容忍性)在分佈式應用中,可能因爲一些分佈式的原因導致系統無法運轉,好的分區容忍性,使應用雖然是一個分佈式系統,但是好像一個可以正常運轉的整體

  • BASE

    • BA: Basic Availability 基本業務可用性;

    • S: Soft state 柔性狀態;

    • E: Eventual consistency 最終一致性;

最終一致性的幾種做法

 

498176-20160628111621249-825372661

單數據庫情況下的事務

如果應用系統是單一的數據庫,那麼這個很好保證,利用數據庫的事務特性來滿足事務的一致性,這時候的一致性是強一致性的。對於java應用系統來講,很少直接通過事務的start和commit以及rollback來硬編碼,大多通過spring的事務模板或者聲明式事務來保證。

基於事務型消息隊列的最終一致性

藉助消息隊列,在處理業務邏輯的地方,發送消息,業務邏輯處理成功後,提交消息,確保消息是發送成功的,之後消息隊列投遞來進行處理,如果成功,則結束,如果沒有成功,則重試,直到成功,不過僅僅適用業務邏輯中,第一階段成功,第二階段必須成功的場景。對應上圖中的C流程。

基於消息隊列+定時補償機制的最終一致性

前面部分和上面基於事務型消息的隊列,不同的是,第二階段重試的地方,不再是消息中間件自身的重試邏輯了,而是單獨的補償任務機制。其實在大多數的邏輯中,第二階段失敗的概率比較小,所以單獨獨立補償任務表出來,可以更加清晰,能夠比較明確的直到當前多少任務是失敗的。對應上圖的E流程。

業務系統業務邏輯的commit/rollback機制

這一點說的話確實不難,commit和rollback是數據庫事務中的比較典型的概念,但是在系統分佈式情況下,需要業務代碼中實現這種,成功了commit,失敗了rollback。

業務應用系統的冪等性控制

爲啥要做冪等呢? 原因很簡單,在系統調用沒有達到期望的結果後,會重試。那重試就會面臨問題,重試之後不能給業務邏輯帶來影響,例如創建訂單,第一次調用超時了,但是調用的系統不知道超時了是成功了還是失敗了,然後他就重試,但是實際上第一次調用訂單創建是成功了的,這時候重試了,顯然不能再創建訂單了。

  • 查詢

查詢的API,可以說是天然的冪等性,因爲你查詢一次和查詢兩次,對於系統來講,沒有任何數據的變更,所以,查詢一次和查詢多次一樣的。

  • MVCC方案

多版本併發控制,update with condition,更新帶條件,這也是在系統設計的時候,合理的選擇樂觀鎖,通過version或者其他條件,來做樂觀鎖,這樣保證更新及時在併發的情況下,也不會有太大的問題。例如update table_xxx set name=#name#,version=version+1 where version=#version# ,或者是 update table_xxx set quality=quality-#subQuality# where quality-#subQuality# >= 0 。

  • 單獨的去重表

如果涉及到的去重的地方特別多,例如ERP系統中有各種各樣的業務單據,每一種業務單據都需要去重,這時候,可以單獨搞一張去重表,在插入數據的時候,插入去重表,利用數據庫的唯一索引特性,保證唯一的邏輯。

  • 分佈式鎖

還是拿插入數據的例子,如果是分佈是系統,構建唯一索引比較困難,例如唯一性的字段沒法確定,這時候可以引入分佈式鎖,通過第三方的系統,在業務系統插入數據或者更新數據,獲取分佈式鎖,然後做操作,之後釋放鎖,這樣其實是把多線程併發的鎖的思路,引入多多個系統,也就是分佈式系統中得解決思路。

  • 刪除數據

刪除數據,僅僅第一次刪除是真正的操作數據,第二次甚至第三次刪除,直接返回成功,這樣保證了冪等。

  • 插入數據的唯一索引

插入數據的唯一性,可以通過業務主鍵來進行約束,例如一個特定的業務場景,三個字段肯定確定唯一性,那麼,可以在數據庫表添加唯一索引來進行標示。

  • API層面的冪等

這裏有一個場景,API層面的冪等,例如提交數據,如何控制重複提交,這裏可以在提交數據的form表單或者客戶端軟件,增加一個唯一標示,然後服務端,根據這個UUID來進行去重,這樣就能比較好的做到API層面的唯一標示。

  • 狀態機冪等

在設計單據相關的業務,或者是任務相關的業務,肯定會涉及到狀態機,就是業務單據上面有個狀態,狀態在不同的情況下會發生變更,一般情況下存在有限狀態機,這時候,如果狀態機已經處於下一個狀態,這時候來了一個上一個狀態的變更,理論上是不能夠變更的,這樣的話,保證了有限狀態機的冪等。

異步回調機制的引入

A應用調用B,在同步調用的返回結果中,B返回成功給到A,一般情況下,這時候就結束了,其實在99.99%的情況是沒問題的,但是有時候爲了確保100%,記住最起碼在系統設計中100%,這時候B系統再回調A一下,告訴A,你調用我的邏輯,確實成功了。其實這個邏輯,非常類似TCP協議中的三次握手。上圖中的B流程。

類似double check機制的確認機制

還是上圖中異步回調的過程,A在同步調用B,B返回成功了。這次調用結束了,但是A爲了確保,在過一段時間,這個時間可以是幾秒,也可以是每天定時處理,再調用B一次,查詢一下之前的那次調用是否成功。例如A調用B更新訂單狀態,這時候成功了,延遲幾秒後,A查詢B,確認一下狀態是否是自己剛剛期望的。上圖中的D流程。

總結

上面的幾點總結,更多的在業務系統中體現,在超複雜的系統中,數據的一致性,不是說簡單的引入啥中間件能夠解決的,更多的是根據業務場景,來靈活應對。

本站部分文章源於互聯網,本着傳播知識、有益學習和研究的目的進行的轉載,爲網友免費提供。如有著作權人或出版方提出異議,本站將立即刪除。如果您對文章轉載有任何疑問請告之我們,以便我們及時糾正。


PS:推薦一個微信公衆號: askHarries 或者qq羣:474807195,裏面會分享一些資深架構師錄製的視頻錄像:有Spring,MyBatis,Netty源碼分析,高併發、高性能、分佈式、微服務架構的原理,JVM性能優化這些成爲架構師必備的知識體系。還能領取免費的學習資源,目前受益良多


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章