Data Consistency Primer

雲應用通常來說,使用的數據很多都是分散的,來自不同的數據倉庫。在這種環境下,管理和保持數據一致性是很複雜的,無論是在併發跟可用性上都可能出問題。開發者有的時候就需要爲了強一致性而犧牲可用性了。這也就意味着開發者需要需要針對應用最終一致性設計一些切面,並且意識到應用並不是所有的時刻都是完全數據一致的。

管理數據一致性

每個web應用和服務都會使用到數據。這數據通常被用戶和組織來做出商務決策的。因此,保證數據的正確和可用,就是數據一致性的要求。數據一致性表示的是所有的應用實例在任何時候的數據都是一致的。有時候也稱數據一致性爲強數據一致性。

在關係型數據庫中,一致性是通過事務模型來實現的,通過使用鎖來在併發實例修改數據的時候對數據進行保護。在強一致性系統中,鎖會阻塞其他的查詢請求。當然,很多關係型數據庫會提供一些機制來讓應用可以訪問還沒有更新的數據。很多應用也會將數據存儲到非關係型數據庫中,或者其他類似策略的文件系統,這種鎖機制也稱之爲消極鎖機制。應用實例會在數據正在修改的時候鎖定數據,當更新完畢後,釋放掉鎖。

在現代的雲應用中,數據很可能是存儲在不同地方的不同的數據倉庫的,有些地理位置可能差異很大。出現這種情況也是出於多種原因:通過增加負載均衡來擴展服務的可擴展性;爲了降低服務時間來擴展服務集羣來減少路由消耗;或者就是爲了做一些數據備份等。

在這種情況下,保證不同數據倉庫的數據一致性就是一個比較困難的問題了。問題就在於,像是序列化以及鎖的使用,都是當且僅當多個應用實例使用的相同的數據倉庫才能正常工作,而且,應用使用鎖的時候,持有鎖的時間都是非常短的。然而,當數據被分割了或者在不同的數據倉庫存在重複的數據的時候,鎖定資源和序列化數據來保證一致性就會變成一個非常昂貴的操作。會給吞吐,響應,以擴展性上帶來巨大的負擔。因此,現代的分佈式應用都不會鎖定被修改的數據,並且採用一種更爲鬆散的方式來維護一致性,也稱之爲最終一致性。

想了解更多關於跨地區的分佈式數據,同地協作的數據或者是複製同步數據的信息,可以參考Data Partitioning Guidance以及Data Replication and Synchronization Guidance

下面的章節描述了更多關於強一致性和最終一致性的信息,以及如何在分佈式環境中使用不同的方法來保證數據的一致性。

強一致性

在強一致性模型中,所有的變動都是原子的。如果一個事務更新多個數據,事務只可能將數據更新全部成功更新到數據庫中,或者失敗的話一條都不更新。在事務開啓和執行期間,其他的併發實例是無法訪問正在更改的數據的,這些併發請求都會阻塞掉。如果數據是複製分發的,那麼強一致性的事務只有當所有的拷貝都成功更新纔會完成。

強一致性模型的目的就是爲了防止應用實例使用不一致的數據(髒讀髒寫)。但是實現這個模型的代價是會影響到可用性,性能,以及擴展性的。在分佈式環境中,如果事務控制的數據位於不同地點的數據倉庫的話,網絡延遲可能極大的影響該事務的性能,這樣會阻塞大量併發實例的訪問。如果網絡的錯誤導致其中的一個或幾個數據倉庫無法訪問,強一致性模型下的事務本身也會持續阻塞直到數據倉庫重新可用。

另外,諸如雲的分佈式環境中,強一致性對於錯誤的包容性是十分差的。舉個例子,如果想要回滾事務和釋放資源鎖,但是這個資源已經由於網絡問題停止響應的話,回滾和釋放資源鎖就很難實現了。在這種情況下,最好是通過其他方法來來解決,比如手動維護一下。

很多雲應用所使用到的數據存儲,都是不支持誇數據倉庫的強一致性的。例如,如果使用Windows Azure Storage的話,跨越多個blob或者table的數據倉庫來實現事務是不可能的。

在雲應用中,開發者只有在絕對必須的情況下才需要去考慮實現強一致性。例如,如果應用所更新的多個數據都在同一個數據倉庫中,那麼實現強一致性的好處可能就更多了,數據在這種情況下很容易僅僅鎖定小段時間,不易造成大規模阻塞。然而,如果更新的數據是通過網絡來進行的,跨越不同的數據倉庫的進行更新的話,最好還是放棄強一致性模型。

在實現了強一致性的系統中進行復制分發數據到不同地點的數據倉庫的話,很可能會在進行復制分發的時候超出強一致性的事務範疇。當複製更新在執行的過程時候,一定程度上的不一致幾乎是無法避免的,但是,數據最終會在同步和複製分發成功之後就重新一致了。關於更多的信息,可以參考Data Replication and Synchronization Guidance.

另一個考慮保證多區域數據強一致性的方案就是通過可擴展的NoSQL數據庫來進行讀寫。這種方法可以不用鎖定數據,當然,也會帶來一定的代價,就是會有關於讀寫數據的額外的複雜性開支。

並不是應用中的所有的數據都需要以使用同一種形式來維護一致性的。應用中可能會使用不同的策略來處理跨數據集的數據一致性問題。具體該使用哪一種策略,還是要取決於應用的業務需求和應用場景了。

最終一致性

最終一致性是一種更爲實用的方法來維護數據一致性。在很多場景下,對事務的要求並不是一定要在某個點傷完成或者回滾並且無更新數據丟失的。在最終一致性模型中,跨越不同數據倉庫的數據更新操作是由自己的調度控制的,無需阻塞應用實例對數據的訪問。

分佈式數據倉庫所遵循的CAP理論就是最終一致性模型的一個驅動器。CAP理論表明:一個分佈式系統只能在同一時間保證三者中的兩者(一致性,可用性,分區容錯性)。實際上,這也就意味着開發者只能:

  • 提供一致的分佈式數據的視圖,但是代價是當數據處於非一致的時候,阻塞任何對於數據的請求。這可能會消耗掉無法預期的時間,尤其是網絡的錯誤造成的無法連接到其他分區的數據中心的時候。
  • 對分佈式數據提供即時的訪問,但是會面臨數據不一致的風險。傳統的數據管理系統都專注於提供強一致性,而基於雲的應用解決方案利用分佈的數據倉庫多數是要基於高可用性,所以因此傾向於最終一致性。

最終一致性未必會作爲一個分佈式系統的明確需求而存在。然而,這通常是一個系統實現高可用性和可擴展性所帶來的一個結果。高可用性和可擴展性的要求一般就無法提供強一致性了。

應用實例可能會查看那些處於操作中的數據的,此時數據是處於臨時性的不一致的。根據系統的不同需求,開發者可能需要設計應用來檢查以及處理這樣的不一致性,如果有必要的話,採取一定的措施來解決這個問題。

最終一致性也會在實用緩存的時候影響數據的一致性。如果遠端的數據倉庫的數據發生了改變,那麼所有基於數據倉庫中數據構建的緩存數據就應該過期了。配置緩存過期策略並且考慮實現Cache-Aside模式可以減少數據不一致的概率。然而,這些方法也不可能完全消除緩存數據的不一致問題,應用只是使用緩存來作爲優化的策略來處理這些不一致問題。

不讓應用時時刻刻保證數據的一致性是十分有價值的。舉例來說,典型的電商網站中,用戶可以瀏覽以及購買商品,而庫存信息也會列給用戶。如果另一個用戶也同時購買了同一個產品,系統中的庫存會減少,但是這個變更信息並不需要及時的反應到其他另一個正在瀏覽該產品的用戶。如果當庫存降至0了,並且第一個用戶在嘗試購買的時候,系統可以再進行提示,告訴用戶該商品已經沒有庫存了,或者將產品後置,告訴用戶送貨時間可能延遲。

實現最終一致性需要考慮的問題

在諸如雲一類的分佈式環境中管理數據的話,更加傾向於最終一致性模型,當然,當使用該模型的時候,也有一些可能面臨的問題需要考慮。下圖總結了其中可能碰到的一些問題。

這裏寫圖片描述

當用戶下了訂單以後,應用實例的操作會跨越多個數據倉庫:

  1. 更新預訂產品的庫存信息
  2. 記錄訂單的詳情信息
  3. 驗證下單的支付信息

在有些情況下,數據倉庫也可能是外部的服務,就好像途中的支付系統一樣。

儘管這些操作構成了一個邏輯事務,但是在這種場景下,實現強一致性是幾乎不可能的。然後,將下單流程作爲最終一致性模型實現,是一種擴展性更好的解決方案。當下單流程中每一步執行的時候,整個系統是非一致的。例如,在庫存已經被更新,但是訂單的信息還沒有落地的時候,系統會暫時的丟失一些庫存。然而,當所有的步驟都執行完畢,系統會返回一個一致的狀態,所有的庫存都可以報賬的。當然,在概念上說明最終一致性模型很簡單,然而開發者是必須要保證系統最後是一致的。換言之,應用必須要保證,無論是所有的步驟都執行完畢,或者是有的步驟失敗的所執行的其他操作,都不會影響系統的最終狀態的一致性。至於開發者實現最終一致性的方案更多的還是基於應用具體問題具體分析的。

重試失敗的步驟

在分佈式環境中,有的時候,不能完成一些列的操作只是出於一些短暫性錯誤(比如說,連接失效)。如果發生這樣的錯誤的話,應用應該意識到這樣的情況只是短時間內的,只需要通過重試之前的失敗的步驟就可以修復這個問題。而一些非短暫性錯誤,比如說虛擬機掛掉了,或者數據庫掛掉了,那麼就不能採用重試的方案來解決,就只能停止執行操作了。具體的方案,可以是簡單的重試兩次,當然,這樣可能會帶來重複更新的問題。想要設計一個方案來防止這類重複現象的發生時很難的,應用應該儘可能的令這些重複不引起問題。

一個很好的解決策略就是把操作中的每一個步驟都變成冪等的。這也就意味着,任何一個成功步驟的多次執行都不會影響系統的狀態。但是這些步驟所構成的業務操作是嚴重依賴於系統的業務邏輯的,並且開發者實現的方式也會嚴重影響數據的結構。定義冪等的操作需要開發者對系統有着很深刻,很透徹的理解。

一些步驟本質上就是冪等的。舉個例子,將某個指定的項目配置爲特殊的值(比如,配置ZipCode爲111111),無論重複多少次,結果都是一樣的。然而,有的時候內在冪等性也不總是發生的。如果系統中包含一些服務,比如說電子商務中的支付服務吧,那麼,最好實現一些僞造的冪等性。通常的解決方案就是將發送給服務的信息關聯一個特殊的ID。服務可以存儲這個ID到本地,只有當這個ID之前沒有處理過的時候,纔會再次執行。這個技術也被稱爲De-Duping(移除重複消息)技術。這個策略,Idempotent Receiver模式中有這個例子。

想了解更多關於冪等方面的信息,可以參考冪等模式

通過冪等命令分割數據

多個應用實例競爭來在同一時間修改相同的數據是另一個使得最終一致性失敗的原因。如果可能的話,開發者應該儘可能設計系統來最小化這類情況。開發者應該嘗試劃分系統來滿足併發實例的在同時執行相同的操作而互不衝突。開發者應該結構化系統,讓系統以冪等的方式來執行業務邏輯而非考慮CRUD之類的操作。想了解更多的信息,可以參考Command and Query Responsibility Segregation模式。CQRS方案中的命令通過Event Sourcing模式。Event-Sourcing是通過驅動一些列事件任務來操作數據的,每個任務只是記錄在一個只追加的隊列中。

想了解更多關於CQRS以及通過事件驅動來實現最終一致性的話,可以參考Command and Query Responsibility Segregation模式

實現補償邏輯

有的時候可能會存在一些依據應用的特殊邏輯,來判斷一個操作是否能夠完成(可能是一系列的應用相關的原因)。在這些情況下,開發者需要實現補償邏輯,來將操作所執行的部分進行重置,在Compensating-Transaction模式中有描述。

在如上圖中所展示的電商模型中,應用所執行的每一步關於訂單的操作都可以記錄下來,以方便之後的重置操作。如果訂單處理失敗了,應用可以通過每一步的補償重置操作來重置之前所執行過的操作。該技術可能並不是完全重置之前操作所帶來的狀態,可能還有有更多的一些涉及到業務相關的邏輯。比如說,如果之前的一個步驟是創建訂單的話,那麼重置的操作可能就不會是把該訂單的記錄刪除,而是僅僅將其狀態置爲取消狀態。

Compensating-Transaction的實現是非常複雜,執行的代價也非常高昂,除非必須,最好不要使用補償事務。

相關的其他模式

在考慮數據一致性的時候,下面的模式以及相關信息也可以進行參考:

  • Compensating-Transaction模式.該模式描述了一旦其中的某個步驟失敗了,關於重置之前所做工作的策略,該模式也是實現了最終一致性模式的。
  • Command and Query責任分離模式.該模式描述了開發者該如何將讀寫數據的操作解耦。該模式可以使用相同數據的不同模型,確保不同模型的數據一致也很重要。
  • Event Sourcing模式.該模式經常配合CQRS模式使用,該模式可以簡化複雜域模型上面的任務,增加性能,擴展性,響應能力。並且爲事務性數據提供一致性,並且能夠保留事件路徑歷史,可以方便的使能補償操作。
  • Data Partitioning Guidance.在大規模雲應用上,數據通常需要分塊管理和訪問。保證分割數據的一致性也是十分重要的。
  • Data Replication and Synchronization Guidance.數據的複製和同步可以很好的增強可用性和性能,確保一致性以及縮小不同地點之間數據傳輸所帶來的消耗。
  • Caching Guidance.應用中緩存的數據和數據倉庫中的數據可能是不一致的。Caching Guidance描述了Cache可以支持的過期政策,來有效減少數據的不一致。
  • Cache-Aside模式.Cache-Aside模式描述瞭如何根據需求去獲取數據。該模式可以用來有效減少重複訪問相同數據的負載。
發佈了44 篇原創文章 · 獲贊 24 · 訪問量 36萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章