什么是一致性模型?
在分布式系統中,C(一致性) 和 A(可用性)始終存在矛盾。若想保證可用性,就必須通過復制、分片等方式冗余存儲。而一旦進行復制,又來帶多副本數據一致性的問題——一個副本的數據更新之后,其他副本必須要保持同步,否則數據不一致就可能導致業務出現問題。
因此,每次更新數據對所有副本進行修改的時間以及方式決定了復制代價的大小。全局同步與性能實際上是矛盾的,而為了提高性能,往往會采用放寬一致性要求的方法。因此,我們需要用一致性模型來理解和推理在分布式系統中數據復制需要考慮的問題和基本假設。
那么什么是一致性模型呢?
一致性模型本質上是進程與數據存儲的約定:如果進程遵循某些規則,那么進程對數據的讀寫操作都是可預期的。
下圖即為 Jepsen 概括的常見的一致性模型:
不可用(Unavailable):粉色代表網絡分區后完全不可用。當出現網絡隔離等問題的時候,為了保證數據的一致性,不提供服務。熟悉 CAP 理論的同學應該清楚,這就是典型的 CP 系統了。
- 嚴格可用 (Sticky Available):黃色代表嚴格可用。即使一些節點出現問題,在一些還沒出現故障的節點,仍然保證可用,但需要保證 client 的操作是一致的。
- 完全可用(Highly Available):藍色代表完全可用。就是網絡全掛掉,在沒有出現問題的節點上面,仍然可用。
一致性模型主要可以分為兩類:能夠保證所有進程對數據的讀寫順序都保持一致的一致性模型稱為強一致性模型,而不能保證的一致性模型稱為弱一致性模型。
強一致性模型
強一致性包含線性一致性和順序一致性,其中前者對安全性的約束更強,也是分布式系統中能保證的最好的一致性。
線性一致性(Linearizable Consistency)
線性一致性是最嚴格的且可實現的單對象單操作一致性模型。在這種模型下,寫入的值在調用和完成之間的某個時間點可以被其他節點讀取出來。且所有節點讀到數據都是原子的,即不會讀到數據轉換的過程和中間未完成的狀態。
要想達到線性一致,需要滿足以下條件:
- 任何一次讀都能讀取到某個數據最近的一次寫的數據。
- 所有進程看到的操作順序都跟全局時鐘下的順序一致。
- 所有進程都按照全局時鐘的時間戳來區分事件的先后,那么必然所有進程看到的數據讀寫操作順序一定是一樣的
我們發現,這個要求十分苛刻,難以在現實中實現。因為各種物理限制使分布式數據不可能一瞬間去同步這種變化。(通信是必然有延遲的,一旦有延遲,時鐘的同步就沒法做到一致。)
順序一致性(Sequential Consistency)
由于線性一致的代價高昂,因此人們想到,既然全局時鐘導致嚴格一致性很難實現,那么我們能否放棄了全局時鐘的約束,改為分布式邏輯時鐘實現呢?
Lamport 在 1979 年就提出的順序一致性正是基于上述原理。順序一致性中所有的進程以相同的順序看到所有的修改。讀操作未必能及時得到此前其他進程對同一數據的寫更新,但是每個進程讀到的該數據的不同值的順序是一致的。
其需要滿足以下條件:
- 任何一次讀寫操作都是按照某種特定的順序。
- 所有進程看到的讀寫操作順序都保持一致。
我們發現他們都能夠保證所有進程對數據的讀寫順序保持一致。那么它與線性一致性有什么不同呢?
盡管順序一致性通過邏輯時鐘保證所有進程保持一致的讀寫操作順序,但這些讀寫操作的順序跟實際上發生的順序并不一定一致。而線性一致性是嚴格保證跟實際發生的順序一致的。
我們以下圖為例:
例子1:線性一致性與順序一致性的區別
圖 a 滿足了順序一致性,但未滿足線性一致性。 從全局時鐘來看,p2 的 read(x,0) 在 p1 的 write(x,4) 之后,但是 p1 卻讀出了舊的數據。因此不滿足線性一致性。但是兩個進程各自的讀寫順序卻是合理的,進程之間也沒有產生沖突,因此從這兩個進程的視角來看,執行流程是這樣的 write(y,2)、read(x,0)、 write(x,4)、read(y,2)。此時滿足順序一致性。
- 圖 b 滿足線性一致性。 因為每個讀操作都讀到了該變量的最新寫的結果,同時兩個進程看到的操作順序與全局時鐘的順序一樣。
- 圖 c 不滿足順序一致性。 因為從進程 P1 的角度看,它對變量 y 的讀操作返回了結果 0。那么就是說,P1 進程的對變量 y 的讀操作在 P2 進程對變量 y 的寫操作之前,x 變量也如此。此時兩個進程存在沖突,因此這個順序不滿足順序一致性。
弱一致性模型
弱一致性是指系統在數據成功寫入之后,不承諾立即可以讀到最新寫入的值,也不會具體承諾多久讀到,但是會盡可能保證在某個時間級別之后,可以讓數據達到一致性狀態。其中包含因果一致性和最終一致性、客戶端一致性。
因果一致性(Causal Consistency)
什么是因果關系呢?
如果事件 B 是由事件 A 引起的或者受事件 A 的影響,那么這兩個事件就具有因果關系。
因果一致性是一種弱化的順序一致性模型,它僅要求有因果關系的操作順序是一致的,沒有因果關系的操作順序是隨機的。
因果一致性的條件包括:
- 所有進程必須以相同的順序看到具有因果關系的讀寫操作。
- 不同進程可以以不同的順序看到并發的讀寫操作。
我們如何確定是否具有因果關系呢?如何傳播這些因果關系呢?
即通過邏輯時鐘來保證兩個寫入是有因果關系的。而實現這個邏輯時鐘的一種主要方式就是向量時鐘。向量時鐘算法利用了向量這種數據結構,將全局各個進程的邏輯時間戳廣播給所有進程,每個進程發送事件時都會將當前進程已知的所有進程時間寫入到一個向量中,而后進行傳播。
最終一致性(Eventual Consistency)
最終一致性是更加弱化的一致性模型,它被表述為副本之間的數據復制完全是異步的,如果數據停止修改,那么副本之間最終會完全一致。而這個最終可能是數毫秒到數天,乃至數月,甚至是“永遠”。
對于最終一致性,我們主要關注以下兩點:
- **最終是多久。**通常來說,實際運行的系統需要能夠保證提供一個有下限的時間范圍。
- **并發沖突如何解決。**一段時間內可能數據可能多次更新,到底以哪個數據為準?通常采用最后寫入成功或向量時鐘等策略。
因為在數據寫入與讀取完全不考慮別的約束條件,因此最終一致性具有最高的并發度,經常被應用于對性能要求高的場景中。
客戶端一致性(Client-centric Consistency)
在最終一致性的模型中,如果客戶端在數據不同步的時間窗口內訪問不同的副本的同一個數據,會出現讀取同一個數據卻得到不同的值的情況。為了解決這個問題,有人提出了以客戶端為中心的一致性模型。
客戶端一致性是站在一個客戶端的角度來觀察系統的一致性。其保證該客戶端對數據存儲的訪問的一致性,但是它不為不同客戶端的并發訪問提供任何一致性保證。
分布式數據庫中,一個節點很可能同時連接到多個副本中,復制的延遲性會造成它從不同副本讀取數據是不一致的。而客戶端一致性就是為了定義并解決這個問題而存在的,這其中包含了寫跟隨讀、管道隨機訪問存儲兩大類別。
寫跟隨讀(Writes-Follow-Reads Consistency)
寫跟隨讀的另一個名字是回話因果(session causal)??梢钥吹剿c因果一致的區別是,它只針對一個客戶端。 即對于一個客戶端,如果一次讀取到了寫入的值 V1,那么這次讀取之后寫入了 V2。從其他節點看,寫入順序一定是 V1、V2。
管道隨機訪問存儲(PRAM,Pipeline Random Access Memory)
管道隨機訪問存儲的名字來源于共享內存訪問模型。其對于單個進程的寫操作都被觀察到是順序的,但不同的進程寫會觀察到不同的順序。
其可拆解為以下三種一致性:
- 單調讀一致性(Monotonic-read Consistency):它強調一個值被讀取出來,那么后續任何讀取都會讀到該值,或該值之后的值,而不會讀取到舊值。
- 單調寫一致性(Monotonic-write Consistency):如果從一個節點寫入兩個值,它們的執行順序是 V1、V2。那么從任何節點觀察它們的執行順序都應該是 V1、V2。
- 讀你所寫一致性(Read-your-writes Consistency):一個節點寫入數據后,在該節點或其他節點上是一定能讀取到這個數據的。
能夠同時滿足以上三種一致性的即為滿足 PRAM。
PRAM、因果一致、線性一致到底有什么區別呢?
例子2:因果一致、順序一致、PRAM 的區別
圖 a 滿足了順序一致性與因果一致性。 圖上的進程都滿足相同的順序與因果關系,因此滿足順序一致性與因果一致性。
- 圖 b 滿足了因果一致性,但未滿足順序一致性。 對于進程 p3 和 p4 其看到的 p1 和 p2 的執行順序不一致,因此不滿足順序一致性。但是由于 p1 與 p2 的寫入沒有任何因果關系,所以此時滿足因果一致性。
- 圖 c 滿足了 PRAM,但未滿足因果一致性。 由于 p2 的 r(x,4) 依賴于 p1 的 w(x,4),此時兩者存在因果關系。然而對于進程 p3 和 p4 而言,其看到的 p1 和 p2 執行順序不同,因此此時并不滿足因果一致性。但此時我們再來分析它們的觀察順序,此時 p4 觀察的到順序是 w(x.7)、w(x,2)、w(x,4)。而 p3 觀察到的是 w(x,2)、w(x,4)、w(x,7)。盡管此時它們對不同進程寫操作觀察的順序不同,但是對于同一個進程的寫操作觀察順序是一致的,因此其滿足 PRAM 一致性。
下圖即為上面討論的幾種一致性模型的關系:
一致性模型之間的關系
事務隔離性
在一開始那張一致性模型圖中,其實是有兩個分支的,一個對應的就是數據庫里面的隔離性(Isolation),另一個其實對應的是分布式系統的一致性(Consistency)。
事務隔離是描述并行事務之間的行為,而一致性是描述非并行事務之間的行為。其實廣義的事務隔離應該是經典隔離理論與一致性模型的一種混合。
潛在問題
如下即數據庫實現中遇到的各種各樣的 isolation 問題。
- P0 Dirty Write(臟寫):一個事務修改了另一個尚未提交的事務已經修改的值。
- P1 Dirty Read(臟讀):一個事務讀取到了另一個執行到一半的事務中修改的值。
- P2 Non-Repeatable Read(不可重復讀):一個事務讀取過程中讀到了另一個事務更新后的結果。
- P3 Phantom(幻讀):某一事務 A 先挑選出了符合一定條件的數據,之后另一個事務 B 修改了符合該條件的數據,此時 A 再進行的操作都是基于舊的數據,從而產生不一致。
- P4 Lost Update(丟失更新):更新被另一個事務覆蓋。
- P4C Cursor Lost Update(游標丟失更新):與 Lost Update 類似,只是發生于 cursor 的操作過程之中。
- A5A Read Skew(讀傾斜):由于事務的交叉導致讀取到了不一致的數據。
- A5B Write Skew(寫傾斜):兩個事務同時讀取到了一致的數據,然后分別進行了滿足條件的修改,但最終結果破壞了一致性。
隔離級別
對于分布式數據庫來說,原始的隔離級別并沒有舍棄,而是引入了一致性模型后,擴寬數據庫隔離級別的內涵。其中共有如下數種隔離級別:
- Read Uncommitted(讀未提交):事務執行過程中能夠讀到未提交的修改。
- Read Committed(讀已提交):事務執行過程中能夠讀到已提交的修改。
- Monotonic Atomic View(單調原子視圖):在 Read Committed 的基礎上加上了原子性的約束,觀測到其他事務的修改時會觀察到完整的修改。
- Cursor Stability(穩定游標):使用 cursor 讀取某個數據時,這個不能被其他事務修改直至 cursor 釋放或事務結束。
- Snapshot Isolation(快照隔離級別):即使其他事務修改了數據,重復讀取都會讀到一樣的數據。
- Repeatable Read(可重復讀):每個事務在獨立、一致的 snapshot 上進行操作,直至提交后其他事務才可見。
- Serializable(串行化):事務按照一定的次序順序執行。
對應的可能發生的問題如下
P(Possible):會發生。
- SP(Sometimes Possible):有時候可能發生。
- NP(Not Possible):不可能發生。
-
數據存儲
+關注
關注
5文章
996瀏覽量
51598 -
模型
+關注
關注
1文章
3479瀏覽量
49945 -
分布式系統
+關注
關注
0文章
147瀏覽量
19521
發布評論請先 登錄
一致性測試系統的技術原理和也應用場景
一行代碼,保障分布式事務一致性—GTS:微服務架構下分布式事務解決方案
順序一致性和TSO一致性分別是什么?SC和TSO到底哪個好?
分布式一致性算法Yac

基于消息通信的分布式系統最終一致性平臺

DSA系統的全局一致性需求分析

分布式大數據不一致性檢測
一致性哈希是什么?為什么它是可擴展的分布式系統架構的一個必要工具
分布式系統的CAP和數據一致性模型
基于自觸發一致性算法的分布式分層控制策略

一種更安全的分布式一致性算法選舉機制

評論