分散式系統中一個重要的問題就是數據複製,數據複製一般是為了增強系統的可用性或提高性能。而實現數據複製的一個主要難題就是保持各個副本的一致性。本文首先討論數據複製的場景中一致性模型如此重要的原因,然後討論一致性模型的含義,最後分析常用的一致性模型。

為什麼需要一致性模型

數據複製主要的目的有兩個:可用性和性能。首先數據複製可以提高系統的可用性。在保持多副本的情況,有一個副本不可用,系統切換到其他副本就會恢復。常用的 MySQL 主備同步方案就是一個典型的例子。另一方面,數據複製能夠提供系統的性能。當分散式系統需要在伺服器數量和地理區域上進行擴展時,數據複製是一個相當重要的手段。有了多個數據副本,就能將請求分流;在多個區域提供服務時,也能通過就近原則提高客戶端訪問數據的效率。常用的 CDN 技術就是一個典型的例子。 但是數據複製是要付出代價的。數據複製帶來了多副本數據一致性的問題。一個副本的數據更新之後,其他副本必須要保持同步,否則數據不一致就可能導致業務出現問題。因此,每次更新數據對所有副本進行修改的時間以及方式決定了複製代價的大小。全局同步與性能實際上是矛盾的,而為了提高性能,往往會採用放寬一致性要求的方法。因此,我們需要用一致性模型來理解和推理在分散式系統中數據複製需要考慮的問題和基本假設。

什麼是一致性模型

首先我們要定義一下一致性模型的術語:

  1. 數據存儲:在分散式系統中指分散式共享資料庫、分散式文件系統等。
  2. 讀寫操作:更改數據的操作稱為寫操作(包括新增、修改、刪除),其他操作稱為讀操作。

下面是一致性模型的定義: 一致性模型本質上是進程與數據存儲的約定:如果進程遵循某些規則,那麼進程對數據的讀寫操作都是可預期的。

上面的定義可能比較抽象,我們用常見的強一致性模型來通俗的解釋一下:在線性一致性模型中,進程對一個數據項的讀操作,它期待數據存儲返回的是該數據在最後一次寫操作之後的結果。這在單機系統裡面很容易實現,在 MySQL 中只要使用加鎖讀的方式就能保證讀取到數據在最後一次寫操作之後的結果。但在分散式系統中,因為沒有全局時鐘,導致要精確定義哪次寫操作是最後一次寫操作是非常困難的事情,因此產生了一系列的一致性模型。每種模型都有效限制了在對一個數據項執行讀操作所應該返回的值。舉個例子:假設記錄值 X 在節點 M 和 N 上都有副本,當客戶端 A 修改了副本 M 上 X 的值,一段時間之後,客戶端 B 從 N 上讀取 X 的值,此時一致性模型會決定客戶端 B 是否能夠讀取到 A 寫入的值。

一致性模型主要可以分為兩類:能夠保證所有進程對數據的讀寫順序都保持一致的一致性模型稱為強一致性模型,而不能保證的一致性模型稱為弱一致性模型

強一致性模型

線性一致性(Linearizable Consistency)

線性一致性也叫嚴格一致性(Strict Consistency)或者原子一致性(Atomic Consistency),它的條件是:

  1. 任何一次讀都能讀取到某個數據最近的一次寫的數據。
  2. 所有進程看到的操作順序都跟全局時鐘下的順序一致。

線性一致性是對一致性要求最高的一致性模型,就現有技術是不可能實現的。因為它要求所有操作都實時同步,在分散式系統中要做到全局完全一致時鐘現有技術是做不到的。首先通信是必然有延遲的,一旦有延遲,時鐘的同步就沒法做到一致。當然不排除以後新的技術能夠做到,但目前而言線性一致性是無法實現的。

順序一致性(Sequential Consistency)

順序一致性是 Lamport(1979)在解決多處理器系統共享存儲器時首次提出來的。參考我之前寫的文章《分散式系統:Lamport 邏輯時鐘》。它的條件是:

  1. 任何一次讀寫操作都是按照某種特定的順序。
  2. 所有進程看到的讀寫操作順序都保持一致。

首先我們先來分析一下線性一致性和順序一致性的相同點在哪裡。他們都能夠保證所有進程對數據的讀寫順序保持一致。線性一致性的實現很簡單,就按照全局時鐘(可以簡單理解為物理時鐘)為參考系,所有進程都按照全局時鐘的時間戳來區分事件的先後,那麼必然所有進程看到的數據讀寫操作順序一定是一樣的,因為它們的參考系是一樣的。而順序一致性使用的是邏輯時鐘來作為分散式系統中的全局時鐘,進而所有進程也有了一個統一的參考系對讀寫操作進行排序,因此所有進程看到的數據讀寫操作順序也是一樣的。

那麼線性一致性和順序一致性的區別在哪裡呢?通過上面的分析可以發現,順序一致性雖然通過邏輯時鐘保證所有進程保持一致的讀寫操作順序,但這些讀寫操作的順序跟實際上發生的順序並不一定一致。而線性一致性是嚴格保證跟實際發生的順序一致的。

弱一致性模型

因果一致性(Causal Consistency)

因果一致性是一種弱化的順序一致性模型,因為它將具有潛在因果關係的事件和沒有因果關係的事件區分開了。那麼什麼是因果關係?如果事件 B 是由事件 A 引起的或者受事件 A 的影響,那麼這兩個事件就具有因果關係。 舉個分散式資料庫的示例,假設進程 P1 對數據項 x 進行了寫操作,然後進程 P2 先讀取了 x,然後對 y 進行了寫操作,那麼對 x 的讀操作和對 y 的寫操作就具有潛在的因果關係,因為 y 的計算可能依賴於 P2 讀取到 x 的值(也就是 P1 寫的值)。 另一方面,如果兩個進程同時對兩個不同的數據項進行寫操作,那麼這兩個事件就不具備因果關係。無因果關係的操作稱為並發操作。這裡只是簡單陳述了一下,深入的分析見我之前寫的文章《分散式系統:向量時鐘》。 因果一致性的條件包括:

  1. 所有進程必須以相同的順序看到具有因果關係的讀寫操作。
  2. 不同進程可以以不同的順序看到並發的讀寫操作。

下面我們來分析一下為什麼說因果一致性是一種弱化的順序一致性模型。順序一致性雖然不保證事件發生的順序跟實際發生的保持一致,但是它能夠保證所有進程看到的讀寫操作順序是一樣的。而因果一致性更進一步弱化了順序一致性中對讀寫操作順序的約束,僅保證有因果關係的讀寫操作有序,沒有因果關係的讀寫操作(並發事件)則不做保證。也就是說如果是無因果關係的數據操作不同進程看到的值是有可能是不一樣,而有因果關係的數據操作不同進程看到的值保證是一樣的。

最終一致性(Eventual Consistency)

最終一致性是更加弱化的一致性模型,因果一致性起碼還保證了有因果關係的數據不同進程讀取到的值保證是一樣的,而最終一致性只保證所有副本的數據最終在某個時刻會保持一致。 從某種意義上講,最終一致性保證的數據在某個時刻會最終保持一致就像是在說:「人總有一天會死」一樣。實際上我們更加關心的是:

  1. 「最終」到底是多久?通常來說,實際運行的系統需要能夠保證提供一個有下限的時間範圍。
  2. 多副本之間對數據更新採用什麼樣的策略?一段時間內可能數據可能多次更新,到底以哪個數據為準?一個常用的數據更新策略就是以時間戳最新的數據為準。

由於最終一致性對數據一致性的要求比較低,在對性能要求高的場景中是經常使用的一致性模型。

以客戶端為中心的一致性(Client-centric Consistency)

前面我們討論的一致性模型都是針對數據存儲的多副本之間如何做到一致性,考慮這麼一種場景:在最終一致性的模型中,如果客戶端在數據不同步的時間窗口內訪問不同的副本的同一個數據,會出現讀取同一個數據卻得到不同的值的情況。為瞭解決這個問題,有人提出了以客戶端為中心的一致性模型。以客戶端為中心的一致性為單一客戶端提供一致性保證,保證該客戶端對數據存儲的訪問的一致性,但是它不為不同客戶端的並發訪問提供任何一致性保證。 舉個例子:客戶端 A 在副本 M 上讀取 x 的最新值為 1,假設副本 M 掛了,客戶端 A 連接到副本 N 上,此時副本 N 上面的 x 值為舊版本的 0,那麼一致性模型會保證客戶端 A 讀取到的 x 的值為 1,而不是舊版本的 0。一種可行的方案就是給數據 x 加版本標記,同時客戶端 A 會緩存 x 的值,通過比較版本來識別數據的新舊,保證客戶端不會讀取到舊的值。

以客戶端為中心的一致性包含了四種子模型:

  1. 單調讀一致性(Monotonic-read Consistency):如果一個進程讀取數據項 x 的值,那麼該進程對於 x 後續的所有讀操作要麼讀取到第一次讀取的值要麼讀取到更新的值。即保證客戶端不會讀取到舊值。
  2. 單調寫一致性(Monotonic-write Consistency):一個進程對數據項 x 的寫操作必須在該進程對 x 執行任何後續寫操作之前完成。即保證客戶端的寫操作是串列的。
  3. 讀寫一致性(Read-your-writes Consistency):一個進程對數據項 x 執行一次寫操作的結果總是會被該進程對 x 執行的後續讀操作看見。即保證客戶端能讀到自己最新寫入的值。
  4. 寫讀一致性(Writes-follow-reads Consistency):同一個進程對數據項 x 執行的讀操作之後的寫操作,保證發生在與 x 讀取值相同或比之更新的值上。即保證客戶端對一個數據項的寫操作是基於該客戶端最新讀取的值。

總結

數據複製導致了一致性的問題,為了保持副本的一致性可能會嚴重地影響性能,唯一的解決辦法就是放鬆一致性的要求。通過一致性模型我們可以理解和推理在分散式系統中數據複製需要考慮的問題和基本假設,便於結合具體的業務場景做權衡。每種模型都有效地限制了對一個數據項執行度操作應返回的值。通常來說限制越少的模型越容易應用,但一致性的保證就越弱。

參考資料

《分散式系統原理與範型》

Distributed systems for fun and profit

Consistency_model

推薦閱讀:

相關文章