掃二維碼與項目經理溝通
我們在微信上24小時期待你的聲音
解答本文疑問/技術咨詢/運營咨詢/技術建議/互聯(lián)網交流
在現實世界中,分布式數據庫的節(jié)點并不總是處于活動狀態(tài)且相互能夠通信的。但是,以上這些故障不應該影響數據庫的可用性。換言之,從用戶的角度來看,整個系統(tǒng)必須像沒有遭到任何故障一樣繼續(xù)運行。系統(tǒng)高可用性是分布式數據庫一個極其重要的特性,甚至在軟件工程中,我們始終致力于實現高可用性,并盡量減少停機時間。

成都創(chuàng)新互聯(lián)堅持“要么做到,要么別承諾”的工作理念,服務領域包括:網站設計制作、做網站、企業(yè)官網、英文網站、手機端網站、網站推廣等服務,滿足客戶于互聯(lián)網時代的孝感網站設計、移動媒體設計的需求,幫助企業(yè)找到有效的互聯(lián)網解決方案。努力成為您成熟可靠的網絡建設合作伙伴!
為了使系統(tǒng)高度可用,系統(tǒng)需要被設計成允許一個或多個節(jié)點的崩潰或不可訪問。為此,我們需要引入如上一講所說的復制技術,其核心就是使用多個冗余的副本來提高系統(tǒng)的可用性。但是,一旦添加了這些副本,我們將面臨使多個數據副本保持同步的問題,并且遭遇故障后如何恢復系統(tǒng)的問題。
這就是 MySQL 復制發(fā)展歷程所引入的 RPO 概念,也就是系統(tǒng)不僅僅要可用,而且數據還需要一致。所以高可用必須要盡可能滿足業(yè)務連續(xù)性和數據一致性這兩個指標。
而我們馬上要介紹的 CAP 理論會告訴我們還有第三個因素——網絡分區(qū)會對可用性產生影響。它會告訴我們可用性和一致性在網絡分區(qū)下是不能同時滿足的。
首先,可用性是用于衡量系統(tǒng)能成功處理每個請求并作出響應的能力??捎眯缘亩x是用戶可以感知到的系統(tǒng)整體響應情況。但在實踐中,我們希望組成系統(tǒng)的各個組件都可以保持可用性。
其次,我們希望每個操作都保持一致性。一致性在此定義為原子一致性或線性化一致性。線性一致可以理解為:分布式系統(tǒng)內,對所有相同副本上的操作歷史可以被看作一個日志,且它們在日志中操作的順序都是相同的。線性化簡化了系統(tǒng)可能狀態(tài)的計算過程,并使分布式系統(tǒng)看起來像在單臺計算機上運行一樣。
最后,我們希望在容忍網絡分區(qū)的同時實現一致性和可用性。網絡是十分不穩(wěn)定的,它經常會分為多個互相獨立的子網絡。在這些子網中,節(jié)點間無法相互通信。在這些被分區(qū)的節(jié)點之間發(fā)送的某些消息,將無法到達它的目的地。
那么總結一下,可用性要求任何無故障的節(jié)點都可以提供服務,而一致性要求結果需要線性一致。埃里克·布魯爾(Eric Brewer)提出的 CAP 理論討論了一致性、可用性和分區(qū)容錯之間的抉擇。
其中提到了,異步系統(tǒng)是無法滿足可用性要求的,并且在存在網絡分區(qū)的情況下,我們無法實現同時保證可用性和一致性的系統(tǒng)。不過我們可以構建出,在盡最大努力保證可用性的同時,也保證強一致性的系統(tǒng);或者在盡最大努力保證一致性的同時,也保證可用性的系統(tǒng)。
這里提到的“最大努力”意味著,如果一切正常,系統(tǒng)可以提供該特性的保證,但是在網絡分區(qū)的情況下,允許削弱和違反這個保證。換句話說,CAP 描述了一種組合性選擇,也就是要有取舍。從 CAP 理論的定義,我們可以擁有以下幾種系統(tǒng)。
CP 系統(tǒng)的場景實現思路是需要引入共識算法,需要大多數節(jié)點參與進來,才能保證一致性。如果要始終保持一致,那么在網絡分區(qū)的情況下,部分節(jié)點可能不可用。
而 AP 系統(tǒng)只要一個副本就能啟動,數據庫會始終接受寫入和讀取服務。它可能最終會丟失數據或產生不一致的結果。這里可以使用客戶端模式或 Session 模型,來提供一致性的解決方案。
使用 CAP 理論時需要注意一些限制條件。
CAP 討論的是網絡分區(qū),而不是節(jié)點崩潰或任何其他類型的故障。這意味著網絡分區(qū)后的節(jié)點都可能接受請求,從而產生不一致的現象。但是崩潰的節(jié)點將完全不受響應,不會產生上述的不一致問題。也就是說,分區(qū)后的節(jié)點并不是都會面臨不一致的問題。而與之相對的,網絡分區(qū)并不能包含真實場景中的所有故障。
CAP 意味著即使所有節(jié)點都在運行中,我們也可能會遇到一致性問題,這是因為它們之間存在連接性問題。CAP 理論常常用三角形表示,就好像我們可以任意匹配三個參數一樣。然而,盡管我們可以調整可用性和一致性,但分區(qū)容忍性是我們無法實際放棄的。
如果我們選擇了 CA 而放棄了 P,那么當發(fā)生分區(qū)現象時,為了保證 C,系統(tǒng)需要禁止寫入。也就是,當有寫入請求時,系統(tǒng)不可用。這與 A 沖突了,因為 A 要求系統(tǒng)是可用的。因此,分布式系統(tǒng)理論上不可能選擇 CA 架構,只能選擇 CP 或者 AP 架構。
如下圖所示,其實 CA 類系統(tǒng)是不存在的,這里你需要特別注意。
圖 1 CAP 理論
CAP 中的可用性也不同于上述的高可用性,CAP 定義對請求的延遲沒有任何限制。此外,與 CAP 相反,數據庫的高可用性并不需要每個在線節(jié)點都可以提供服務。
CAP 里面的 C 代表線性一致,除了它以外,還有其他的一致模式,我們現在來具體介紹一下。
一致性模型是分布式系統(tǒng)的經典內容,也是入門分布式數據庫的重要知識點。但很少有人知道,其實一致性模型來源于單機理論中的共享內存。
從用戶的角度看,分布式數據庫就像具有共享存儲的單機數據庫一樣,節(jié)點間的通信和消息傳遞被隱藏到了數據庫內部,這會使用戶產生“分布式數據庫是一種共享內存”的錯覺。一個支持讀取和寫入操作的單個存儲單元通常稱為寄存器,我們可以把代表分布式數據庫的共享存儲看作是一組這樣的寄存器。
每個讀寫寄存器的操作被抽象為“調用”和“完成”兩個動作。如果“調用”發(fā)生后,但在“完成”之前該操作崩潰了,我們將操作定義為失敗。如果一個操作的調用和完成事件都在另一個操作被調用之前發(fā)生,我們說這個操作在另一個操作之前,并且這兩個操作是順序的;否則,我們說它們是并發(fā)的。
如下圖所示,a)是順序操作,b)和 c)是并發(fā)操作。
圖 2 順序操作&并發(fā)操作
多個讀取或寫入操作可以同時訪問一個寄存器。對寄存器的讀寫操作不是瞬間完成的,需要一些時間,即調用和完成兩個動作之間的時間。由不同進程執(zhí)行的并發(fā)讀/寫操作不是串行的,根據寄存器在操作重疊時的行為,它們的順序可能不同,并且可能產生不同的結果。
當我們討論數據庫一致性時,可以從兩個維度來區(qū)別。
滯后性。它是數據改變的時刻與其副本接收到數據的時刻。這是上一講所介紹的復制延遲場景,一般被歸類為“客戶端一致性”范疇。我們將在“15 | 再談一致性:除了 CAP 之外的一致性模型還有哪些”中進一步討論。
順序性。討論的是各種操作在系統(tǒng)所有副本上執(zhí)行的順序狀態(tài)。這是本講一致性模型所討論的重點。
現在我們對順序性再做進一步的探討。
當面對一系列讀寫操作時,作為人類,我們對它們的執(zhí)行順序是有一個主觀判斷的。甚至,對于一個單機數據而言,這些操作的順序也是可以確定的。但是,在分布式系統(tǒng)中做出這種判斷就不是那么容易了,因為很難知道什么時候確切地發(fā)生了什么,并且很難在整個集群中立刻同步這些操作。
為了推理操作順序并指出真正的結果,我們必須定義一致性模型來保障順序性。
我們怎么來理解模型中“保障”的含義呢?它是將一致性模型視為用戶與數據庫之間的一種約定,每個數據庫副本如何做才能滿足這種順序保障?并且用戶在讀取和寫入數據時期望得到什么?也就是說,即使數據是被并發(fā)讀取和寫入的,用戶也可以獲得某種可預測的結果。
需要注意,我們將要討論單一對象和單一操作一致性模型,但現實的數據庫事務是多步操作的,我們將在下面“事務與一致性”部分進一步討論。
下面我按照順序性的保障由強到弱來介紹一致性模型。
嚴格的一致性類似于不存在復制過程:任何節(jié)點的任何寫入都可立即用于所有節(jié)點的后續(xù)讀取。它涉及全局時鐘的概念,如果任何節(jié)點在時刻 T1 處寫入新數據 A,則所有節(jié)點在 T2 時刻(T2 滿足 T2>T1),都應該讀到新寫入的 A。
不幸的是,這只是理論模型,現實中無法實現。因為各種物理限制使分布式數據不可能一瞬間去同步這種變化。
線性一致性是最嚴格的且可實現的單對象單操作一致性模型。在這種模型下,寫入的值在調用和完成之間的某個時間點可以被其他節(jié)點讀取出來。且所有節(jié)點讀到數據都是原子的,即不會讀到數據轉換的過程和中間未完成的狀態(tài)。
線性一致需要滿足的是,新寫入的數據一旦被讀取出來,那么所有后續(xù)的讀操作應該能讀取到這個數據。也就是說,一旦一個讀取操作讀到了一個值,那么后續(xù)所有讀取操作都會讀到這個數值或至少是“最近”的一個值。
上面的定義來自早期的論文,我將里面的關鍵點提煉一下,如下所示。
下面我通過一個例子來說明線性一致性。
現在有三個節(jié)點,其中一個共享變量 x 執(zhí)行寫操作,而第三個節(jié)點會讀取到如下數值。
下圖正是現象一致性的直觀展示。
圖 3 線性一致性
線性一致性的代價是很高昂的,甚至 CPU 都不會使用線性一致性。有并發(fā)編程經驗的朋友一定知道 CAS 操作,該操作可以實現操作的線性化,是高性能并發(fā)編程的關鍵,它就是通過編程手段來模擬線性一致。
一個比較常見的誤區(qū)是,使用一致性算法可以實現線性一致,如 Paxos 和 Raft 等。但實際是不行的,以 Raft 為例,算法只是保證了復制 Log 的線性一致,而沒有描述 Log 是如何寫入最終的狀態(tài)機的,這就暗含狀態(tài)機本身不是線性一致的。
這里推薦你閱讀 TiKV 關于線性一致的實現細節(jié),由于線性一致性價比不高,這里就不進行贅述了,我們接下來說說順序一致性和因果一致性。
由于線性一致的代價高昂,因此人們想到,既然全局時鐘導致嚴格一致性很難實現,那么順序一致性就是放棄了全局時鐘的約束,改為分布式邏輯時鐘實現。順序一致性是指所有的進程以相同的順序看到所有的修改。讀操作未必能及時得到此前其他進程對同一數據的寫更新,但是每個進程讀到的該數據的不同值的順序是一致的。
下圖展示了 P1、P2 寫入兩個值后,P3 和 P4 是如何讀取的。以真實的時間衡量,1 應該是在 2 之前被寫入,但是在順序一致性下,1 是可以被排在 2 之后的。同時,盡管 P3 已經讀取值 1,P4 仍然可以讀取 2。但是需要注意的是這兩種組合:1->2 和 2 ->1,P3 和 P4 從它們中選擇一個,并保持一致。下圖正是展示了它們讀取順序的一種可能:2->1。
圖 4 順序一致性
我們使用下圖來進一步區(qū)分線性一致和順序一致。
圖 5 區(qū)分線性一致和順序一致
其中,圖 a 滿足了順序一致性,但是不滿足線性一致性。原因在于,從全局時鐘的觀點來看,P2 進程對變量 x 的讀操作在 P1 進程對變量 x 的寫操作之后,然而讀出來的卻是舊的數據。但是這個圖卻是滿足順序一致性,因為兩個進程 P1 和 P2 的一致性并沒有沖突。
圖 b 滿足線性一致性,因為每個讀操作都讀到了該變量的最新寫的結果,同時兩個進程看到的操作順序與全局時鐘的順序一樣。
圖 c 不滿足順序一致性,因為從進程 P1 的角度看,它對變量 y 的讀操作返回了結果 0。那么就是說,P1 進程的對變量 y 的讀操作在 P2 進程對變量 y 的寫操作之前,x 變量也如此。因此這個順序不滿足順序一致性。
在實踐中,你就可以使用上文提到的一致性算法來實現順序一致。這些算法可以保證操作在每個節(jié)點都是按照一樣的順序被執(zhí)行的,所以它們能保證順序一致。
如 Google Megastore 這類系統(tǒng)都是使用 Paxos 算法實現了順序一致性。也就是說在 Megastore 內部,如果有一個數據更新,所有節(jié)點都會同步更新,且操作在各個節(jié)點上執(zhí)行順序是一致的。
相比于順序一致性,因果一致性的要求會低一些:它僅要求有因果關系的操作順序是一致的,沒有因果關系的操作順序是隨機的。
因果相關的要求有如下幾點。
那么,為什么需要因果關系,以及沒有因果關系的寫法如何傳播?下圖中,進程 P1 和 P2 進行的寫操作沒有因果關系,也就是最終一致性。這些操作的結果可能會在不同時間,以亂序方式傳播到讀取端。進程 P3 在看到 2 之前將看到值 1,而 P4 將先看到 2,然后看到 1。
圖 6 因果一致性
而下圖顯示進程 P1 和 P2 進行因果相關的寫操作并按其邏輯順序傳播到 P3 和 P4。因果寫入除了寫入數據外,還需要附加一個邏輯時鐘,用這個時鐘保證兩個寫入是有因果關系的。這可以防止我們遇到上面那張圖所示的情況。你可以在兩個圖中比較一下 P3 和 P4 的歷史記錄。
圖 7 邏輯時鐘
而實現這個邏輯時鐘的一種主要方式就是向量時鐘。向量時鐘算法利用了向量這種數據結構,將全局各個進程的邏輯時間戳廣播給所有進程,每個進程發(fā)送事件時都會將當前進程已知的所有進程時間寫入到一個向量中,而后進行傳播。
因果一致性典型案例就是 COPS 系統(tǒng),它是基于 causal+一致性模型的 KV 數據庫。它定義了 dependencies,操作了實現因果一致性。這對業(yè)務實現分布式數據因果關系很有幫助。另外在亞馬遜 Dynamo 基于向量時鐘,也實現了因果一致性。
現在我們談論了一致性模型,但是它與數據庫領域之中的事務有什么區(qū)別呢?我先說結論:有關系但又沒有關系。
怎么理解呢?我先來論證它們之間的無關性。
ACID 和 CAP 中的“C”是都是一致性,但是它們的內涵完全不同。其中 ADI 都是數據庫提供的能力保障,但是 C(一致性)卻不是,它是業(yè)務層面的一種邏輯約束。
以轉賬這個最為經典的例子而言,甲有 100 元 RMB,乙有 0 元 RMB,現在甲要轉給乙 30 元。那么轉賬前后,甲有 70,乙有 30,合起來還是 100。顯然,這只是業(yè)務層規(guī)定的邏輯約束而已。
而對于 CAP 這里的 C 上文已經有了明確說明,即線性一致性。它表示副本讀取數據的即時性,也就是對“何時”能讀到“正確”的數據的保證。越是即時,說明系統(tǒng)整體上讀取數據是一致的。
那么它們之間的聯(lián)系如何呢?其實就是事務的隔離性與一致模型有關聯(lián)。
如果把上面線性一致的例子看作多個并行事務,你會發(fā)現它們是沒有隔離性的。因為在開始和完成之間任意一點都會讀取到這份數據,原因是一致性模型關心的是單一操作,而事務是由一組操作組成的。
現在我們看另外一個例子,這是展示事務缺乏一致性后所導致的問題。
圖 8 事務與一致性
其中三個事務滿足隔離性。可以看到 T2 讀取到了 T1 入的值。但是這個系統(tǒng)缺乏一致性保障,造成 T3 可以讀取到早于 T2 讀取值之前的值,這就會造成應用的潛在 Bug。
那現在給出結論:事務隔離是描述并行事務之間的行為,而一致性是描述非并行事務之間的行為。其實廣義的事務隔離應該是經典隔離理論與一致性模型的一種混合。
比如,我們會在一些文獻中看到如“one-copy serializability”“strong snapshot isolation”,等等。前者其實是 serializability 隔離級別加上順序一致,后者是 snapshot 隔離級別加上線性一致。
所以對分布式數據庫來說,原始的隔離級別并沒有舍棄,而是引入了一致性模型后,擴寬數據庫隔離級別的內涵。
今天的內容較長,不過已經精煉很多了。我們從高可用性入手,介紹了 CAP 理論對于分布式模型評估的影響;而后重點介紹了一致性模型,這是核心,用來幫助你評估分布式數據庫的特性。
最后我介紹了事務隔離級別與一致性模型之間的區(qū)別與聯(lián)系,幫助你認清分布式數據庫下的事務隔離級別的概念。?

我們在微信上24小時期待你的聲音
解答本文疑問/技術咨詢/運營咨詢/技術建議/互聯(lián)網交流