What is Consistency
一致性(Consistency)一直是分佈式系統裏一個很重要的話題。
在存儲系統中,爲了避免數據丟失,我們都會對數據進行持久化。
對數據進行持久化可以避免宕機帶來的數據丟失問題,但是不能解決單機永久性故障的問題。存儲系統作爲基礎設施,在單機上持久化是遠遠不夠的,我們需要將數據複製到多臺機器上以提升系統的可用性和可靠性。
一旦數據被複制到多個節點,那麼就產生了一致性的問題。
系統需要定義一組協議來規定用戶讀寫多副本的行爲,這組協議稱之爲一致性模型(Consistency Model)。在分佈式系統領域,談論一致性時通常談論的都是一致性模型。
Consistency Model
不同的一致性模型對系統的行爲和表現有不同的約束。
Strict Consistency
Strict Consistency是最強的一致性模型,要求任何讀取操作都能讀取到最新的值,換句話說,要求任何寫入操作立即同步給所有進程。在分佈式系統中,數據的同步是需要時間的,因此在分佈式系統下無法嚴格實現Strict Consistency。除非讓所有的讀寫操作都只在一個進程的一個線程中執行或者,讀寫操作被鎖保護起來。(根據CAP的原理,這個一致性模型在沒有犧牲可用性的前提下是不能得到滿足的。 性能也是不可接受的:所有的寫操作需要同步到所有節點之後再返回給客戶端。)
Strict Consistency如上圖所示,在時間軸上,一旦有進行寫入了x=1,其他進程立即能讀到x=1的值。
- 在W(x)1之後,W(x)2之前,所有進程讀取到的x的值一定是1
- 在W(x)2之後,W(x)3之前,所有進程讀取到的x的值一定是2
- 在W(x)3之後,所有進程讀取到的x的值一定是3
Sequential Consistency
Sequential Consistency是比Strict Consistency弱一些的一致性模型,要求:
- 進程內,對同一個變量的讀寫保持順序
- 進程間,“看到”的變量的變更順序是一致的(不要求和“物理時間”下的順序保持一致)
以Consistency Model中的例子舉例,“看到”以下幾種數據的變更順序都是滿足Sequential Consistency的:
- x=1, x=2, x=3:滿足Strict Consistency,自然滿足Sequential Consistency
- x=2, x=1, x=3:滿足了P1中同一個變量的變更順序,不同進程“看到”的順序一致
- x=1, x=3, x=2:同上
(進程間的事件的順序可以參看《Time, Clocks, and the Ordering of Events in a Distributed System》)
Linearizable Consistency
Linearizable Consistency比Sequential Consistency更嚴格一些:
- 進程內,對同一個變量的讀寫操作保持順序
- 進程間,“看到”的變量的變更順序和全局“物理時鐘”下的順序是一致的
即Linearizable Consistency是Sequential Consistency的特例,除了滿足所有進程讀到的變更順序是相同,還要求這個順序和全局時鐘下的順序是一致的。
和全局時鐘下的順序保持一致容易理解,即事件的順序和它們在客觀的物理時間下發生的時間順序是一致的。但是如果事件是併發發生的,如何滿足這個順序要求:
如上圖,P0的write x=1操作和P1的read x=0/1有重疊的部分,那麼之間的先後關係是怎樣的?
對於併發的情況Linearizable Consistency並不要求他們之間有確定的順序,即認爲P0的write x=1先於P1的read x=0/1或者P1的read x=0/1先於P0的write x=1都是合理的,但是P0~P3進程看到的這兩個操作的順序必須是確定的、一致的。
上圖的例子中,滿足以下情況的序列都是滿足Linearizable Consistency的:
- w1 r1 r1 r1(P0->P1->P2->P3或P0->P1->P3->P2)
- r0 w1 r1 r1(P1->P0->P2->P3或P1->P0->P3->P2)
- r0 r0 w1 r1(P1->P2->P0->P3)
以上三種一致性模型:Strict Consistency、Linearizable Consistency、Sequential Consistency都是強一致模型。
Causal Consistency
Causal Consistency是一種弱一致性模型,僅要求有因果關係的操作順序性得到保證,非因果關係的操作順序性沒有要求。
具體如下:
- 本地順序:統一進程中,事件的執行順序即爲本地的因果順序
- 異地順序:如果讀操作返回的是寫操作的值,那麼該寫操作一定在讀操作之前
- 閉包傳遞:如果a->b,b->c,那麼a->c
(Lamport在《Time, Clocks, and the Ordering of Events in a Distributed System》中描述的happen-before關係及其傳遞閉包)
騰訊朋友圈的例子
在infoq分享的騰訊朋友圈的設計中,他們在設計數據一致性的時候,使用了因果一致性這個模型。用於保證對同一條朋友圈的回覆的一致性,比如這樣的情況:
- A發了朋友圈內容爲梅里雪山的圖片。
- B針對內容a回覆了評論:“這裏是哪裏?”
- C針對B的評論進行了回覆:“這裏是梅里雪山”
那麼,這條朋友圈的顯示中,顯然C針對B的評論,應該在B的評論之後,這是一個因果關係,而其他沒有因果關係的數據,可以允許不一致。
微信的做法是:
- 每個數據中心,都自己生成唯一的、遞增的數據ID,確保能排重。在下圖的示例中,有三個數據中心,數據中心1生成的數據ID模1爲0,數據中心1生成的數據ID模2爲0,數據中心1生成的數據ID模3爲0,這樣保證了三個數據中心的數據ID不會重複全局唯一。
- 每條評論都比本地看到所有全局ID大,這樣來確保因果關係。
上圖是Causal Consistency在微信朋友圈中的應用。
比Causal Consistency更弱的一致性模型還有Eventual Consistency(最終一致),比如MySQL的異步複製。
Eventual Consistency:存儲系統保證如果沒有新的寫操作,那麼最終,所有的讀操作都能讀到一致的數據,這裏強調對一個數據項的修改最終會收斂。
總結
本文簡單的描述了分佈式系統中一致性問題的由來,並介紹了幾種一致性模型。其中,Strict Consistency要求最爲嚴格,是現實環境中難以滿足的一種一致性模型,除非犧牲可用性。Linearizable Consistency略弱於Strict Consistency,不要求寫入操作立即可見,但是要求寫入操作保持和全局時鐘下的順序一致。Sequential Consistency則更弱一些,不要求寫入操作保持和全局時鐘下的順序一致,但是要求所有進程看到的寫入操作的順序是一致的。Strict Consistency、Linearizable Consistency、Sequential Consistency都被認爲是強一致的模型。
Causal Consistency被認爲是一種若一致模型,它只要求有因果關係的事件之間保持順序,詳細可以參看Lamport在《Time, Clocks, and the Ordering of Events in a Distributed System》中描述的happen-before關係及其傳遞閉包。
Eventual Consistency是最終一致,只要求在沒有新寫入的情況下,最終所有數據達成一致,常見於一些異步複製的系統。