如何理解ZooKeeper的順序一致性?

轉:blog.csdn.net/cadem/artic…html

Sequential consistency分析

2017 餓了麼作異地多活,個人團隊承擔 ZooKeeper 的異地多活改造。在此期間我聽到 2 種不一樣的關於一致性的說法。程序員

  • 一種說法是 ZooKeeper 是最終一致性,由於因爲多副本、以及保證大多數成功的 Zab 協議,當一個客戶端進程寫入一個新值,另一個客戶端進程不能保證立刻就能讀到這個值,可是能保證最終能讀取到這個值。面試

  • 另一種說法是 ZooKeeper 的 Zab 協議相似於 Paxos 協議,而且提供了強一致性。算法

每當我聽到這 2 種說法,我都想上去糾正一下,「不對,ZooKeeper 是順序一致性 (Sequential consistency)」。apache

可是解釋起來太複雜了,須要一篇長文來講明。一直想寫這篇文章說明這個說法,可是一直沒寫,餓了麼的異地多活項目結束這麼長時間了,終於擠一些時間把它寫出來,和你們一塊兒討論一下。編程

從 ZooKeeper 的文檔中咱們能夠看到,ZooKeeper 文檔中明確寫明它的一致性是 Sequential consistency(參考連接見文末)。數組

什麼是 Sequential consistency 呢?

Sequential consistency 的是 Lamport 在 1979 年首次提出的。(參看他的論文 How to make a multiprocessor computer that correctly executes multiprocess programs) 論文中定義,當知足下面這個條件時就是 sequential consistency:緩存

the result of any execution is the same as if the operations of all the processors were executed in some sequential order, and the operations of each individual processor appear in this sequence in the order specified by its program.bash

這段英文定義很晦澀(這是 Lomport 大神的一貫的風格,嚴謹但晦澀,Paxos 協議也是如此),我第一次看到這段定義時的感受就是:「這是什麼鬼?」。爲何每一個英文單詞我都認識,可是怎麼就是不知道他在說什麼。第一次看到這句話和我有同感的小夥伴舉個手。多線程

本文後面我再把這段英文定義翻譯成中文,如今咱們先來看看這篇論文的標題和定義中出現的一個關鍵詞,來講明一下 sequential consistency 的應用範圍。論文的標題和這段定義中包含 multiprocessor 這個詞,Multiprocessor 是多處理器的意思。

從這個關鍵字上來看,sequential consistency 是用來定義多處理器和跑在多處理器上的程序的一個特性。Lomport 這篇論文的標題能夠翻譯成,「如何讓具備多處理器的計算機的正確執行多進程程序」,也就是說若是一個多核處理器具備 sequential consistency 的特性,這個多核處理器就能夠正確的運行,後面咱們來解釋這個正確運行是什麼意思(也就是本文後面講到的 Sequential consistency 的做用)。從這個標題中咱們還能夠看出,Sequential consistency 應該是個 併發編程(concurrent programming)領域 的概念。

可是咱們如今經常在分佈式系統領域討論 Sequential consistency,好比本文主要要討論 Zookeeper(Zookeeper 很明顯是一個分佈式系統)的一致性。實際上,多核處理器上的運行的多個程序,其實也是一種分佈式系統(Lomport 在他的這篇< Time, Clocks, and the Ordering of Events in a Distributed System >分佈式系統的開山之做中也闡述了這個觀點)。

因此雖然 Sequential consistency 最先在併發編程中提出,可是它能夠 應用在分佈式系統 中,好比本文討論的 Zookeeper 這種分佈式存儲存儲系統。另一個比較重要的 Linearizability(線性一致性),也是在併發編程中最先提出的,目前也被普遍的應用在分佈式系統領域中。

下面咱們要來翻譯上面那段晦澀的定義。作這段定義的翻譯讓我找到了上學時作閱讀理解的感受。我先不直接翻譯,由於就算我把它翻譯成中文,我估計不少人仍是不明白是什麼意思。仍是會有那種感受,爲毛每一箇中文字我都懂,可仍是不知道在說什麼。

首先,我來解釋一些個別的詞。第一個,any execution,any execution 是什麼意思?你有多個程序(program)在多核處理器上運行,例如你有 2 個程序,

第一個程序叫 P1,它的代碼以下:
    P1_write(x);
    P1_read(y);
第二個程序叫 P2,代碼以下:
    P2_write(u);
    P2_read(v);
複製代碼

從理論上來說,2 個程序運行在 2 個獨立的處理器的核上,有多少種執行的可能那?我列舉其中幾種來舉例說明。

第 1 種:
    P1---write(x)--------read(y)--------
    P2-----------write(u)-------read(v)-
第 2 種:
    P1----------write(x)-read(y)--------
    P2--write(u)----------------read(v)-
第 3 種:
    P1---read(y)----------write(x)------
    P2-----------write(u)---------read(v)-
複製代碼

咱們有 24 中可能的執行順序,也就是這 4 個操做任意的排列組合,也就是 4!=24。相似第一種和第二種這樣的可能性很好理解。爲何會出現像第 3 種這樣的可能的執行那?那是由於就算是在同一個程序中,因爲處理會有多級的緩存,以及處理器中 coherence 的存在,雖然你的程序中是先 write 後 read,在內存中真正生效的順序,也有多是先 read 後 write。

其實還會出現相似下面這樣的執行,2 個操做在 2 個處理器上同時執行。 P1--write(x)-read(y)-------- P2--write(u)--------read(v)- 若是加上同時運行的這種狀況,那就有更多種可能性。個人算數很差,這裏我就再也不繼續算了,由於到底有多少個不重要,重要的是你知道有不少種可能性就能夠了。那麼定義中的"any execution",就是指任意一種可能的執行,在定義中也能夠理解爲全部的這些可能的執行。 接下來仍是不翻譯定義,咱們再來解釋一個詞 --sequential order。什麼叫 sequential order?咱們來翻一下英語詞典(感受更像是在作閱讀理解了)。 sequential: 連續的;相繼的;有順序的 order: 命令;順序;規則;[貿易] 定單

sequential order-- 有順序的順序,這個是什麼鬼? 其實 sequential 是有一個接一個的意思,在處理器的這種上下文中,sequential 就是指 操做(operartion)一個接一個的執行,也就是順序執行,而且沒有重疊。Order 是指通過必定的調整,讓某樣東西按照必定的規則變得有序。好比,在算法中的排序算法就是 ordering,就是讓數組這個東西按照從大到小的規則或則從小到大的規則變得有序。那麼 sequential order 就是指讓操做(operation)按照一個接一個這樣的規則排列,而且沒有重疊。 仍然說上面的例子,若是把 4 個操做,按一個接一個的規則排列,咱們這時就能夠獲得 4!的排列組合個可能的排列(order),仍然,到底有多少個不重要。

好比:

P1_write(x);P1_read(y);P2_write(u);P2_read(v);
    P1_read(y);P1_write(x);P2_write(u);P2:read(v);
    P2_write(u);P2_read(v);P1_read(y);P1:write(x);
複製代碼

我這裏只列舉其中 3 個,其餘的你們能夠本身排一下。 重點來了,其實 sequential order 就是讓這 4 個操做一個接一個的順序執行,而且沒有重疊。注意這個排列不是真實的執行,真實的執行是 any execution,這裏說的是邏輯上的假設,這也就是爲何定義有一個 as if。

作了這麼多的鋪墊,下面咱們開始翻譯定義中的第一句話:

任意一種可能的執行的效果和某一種全部的處理器上的操做按照順序排列執行的效果是同樣的。

注意,這裏 some 在這裏是某一的意思,不是一些,由於 order 是單數。(在作閱讀理解) 這就話的意思就是說,一個處理器要知足這個條件,就要可以只容許知足這個條件的那些可能的執行存在,其餘不知足的可能的執行都不會出現。

從第一句話中咱們能夠看出,一種多核處理器要想知足 sequential consistency,那麼多個程序在多個核運行效果"等同"於在一個核上順序執行全部操做的效果是差很少的。若是這樣的話,其實多核的威力基本就消失了。因此不管是從 Lomport 寫這篇論文的 1979,仍是如今,沒有任何一個現實的多核處理器,實現了 sequential consistency。

那麼爲何 Lomport 大神提出這樣一個不現實的概念那?(我要注意 Lomport 寫這篇論文時,並無把它引伸到分佈式系統領域,就是針對多核處理器,併發編程領域提出的)咱們如今先不說,稍後在論述。

這裏還要注意的一點是,在個人翻譯裏用了效果一詞,但實際上英文原文定義中用的是 result(結果)一詞。那效果和結果有什麼區別嗎?咱們解釋一下什麼叫執行結果?無論是任何真實的執行,仍是某種通過順序排序後的假設執行,程序會產生必定的結果,好比 print 出來的結果(result)。實際上定義中說的是結果同樣。

若是定義中用效果的話,那麼這個定義就只是一個定性的定義,若是用結果的話,那這個定義就是一個定量的定義。定量的,也就是說是能夠經過數學證實的。從這點咱們就能夠看出,大神就是不同,任何理論都是能夠經過數學證實是正確的。文章後面還會提到證實的事情,咱們這裏再賣個關子。 到這裏,咱們第一句定義的更準確翻譯是:

任意一種可能的執行的結果和某一種全部的處理器的操做按照順序排列執行的結果是同樣的

這裏咱們還要注意一點的是,結果同樣就意味着,若是有人真的要實現一種 sequential consistency 的多核處理器的話,由於要保證結果同樣,因此他是有必定的空間來優化,而不會徹底是一個核順序執行的效果。可是估計這種優化也是很是有限的。

好了,咱們終於把最難的第一話解釋完了,你們能夠鬆口氣,第二句就很是簡單了。咱們仍是先解釋一個詞再完整的翻譯。這個詞就是第二句中出現的 sequence。咱們剛剛解釋過的 sequential order 是順序排序(於就是按一個接一個排序),其實這是一個動做,動做會產生結果,它的結果產生了一個操做(operation)的隊列。第二句中出現的 sequence 就是指這個操做(operation)的隊列。

好,那第二句的翻譯就是:

而且每一個獨立的處理器的操做都會按照程序指定的順序出如今操做隊列中。

也就是說若是程序裏是先 write(x);後 read(y); 那麼只有知足這個順序的操做隊列是符合條件的。這樣,咱們剛剛說的不少可能的執行就少了不少,這裏我也就不計算少了多少,仍是那句話,數量不重要,反正是有,並且變少了。那麼第二句意味這什麼?意味着若是一個多核處理器實現了 sequential consistency,那麼這種多核處理器基本上就告別自(緩)行(存)車了。這裏我還繼續賣關子,連緩存這種最有效提升處理器性能的優化都沒了,大神爲何要提出這個概念?

好了,到這裏咱們能夠把 2 句翻譯合起來,完整的看一下:

任意一種可能的執行的結果和某一種全部的處理器的操做按照順序排列執行的結果是同樣的,而且每一個獨立的處理器的操做都會按照程序指定的順序出如今操做隊列中。

從這個定義中,咱們能夠看出,這個概念的核心就是 sequential order,這也就是爲何 Lomport 老爺子,把這種一致性模型稱之爲 sequential consistency。能夠說這個命名是很是貼切的。不知道這種貼切對於以英語爲母語的人來講是否是更好理解一些,應該不會出現"順序的順序是什麼鬼"的這種狀況。若是你看完這篇文章,也以爲 sequential 很貼切的話,那就說明我講清楚了。 接下來咱們舉個具體的例子,再來講明一下。

execution A
    P0 writex=1-------------------------------
    P1 -------write x=2----------------------
    P2 -----------------read x==1--read x==2
    P3 -----------------read x==1--read x==2

    sequetial order: P0_write x=1,P3_read x==1,P4_read x==1,P1_write x=2,P3_read x==2,P4_read x==2

    execution B
    P0 write=1-------------------------------
    P1 -------write x=2----------------------
    P2 -----------------read x==2--read x==1
    P3 -----------------read x==2--read x==1

    sequetial order: P1_write x=2,P3_read x==2,P4_read x==2,P0_write x=1,P3_read x==1,P4_read x==1

    execution C
    P0 write=1-------------------------------
    P1 -------write x=2----------------------
    P2 -----------------read x==1--read x==2
    P3 -----------------read x==2--read x==1
複製代碼

sequetial order: 你找不出一個符合定義中 2 個條件的一種 order。 因此說若是一個多核處理器只容許 execution A 和 B 出現,不容許 C 出現,那麼這個多核處理器就是 sequetial consistency 的。若是它容許 C 出現,那它就不是 sequetial consistency。

到這裏咱們已經完整的講完什麼是 sequetial consistency。可是,細心的朋友可能會問,若是你的 program 是的多線程的程序怎麼辦那?那麼咱們再把定義中最後的一個細節解釋一下:program 這個詞。Program 是指能夠直接運行在處理器上的指令序列。這個並非 Pogram 的嚴格定義,可是我要指出的是這個 Program 是在操做系統都沒有的遠古時代就存在的概念,這個定義中 prgram 就是指那個時代的 program。這個 Program 裏沒有進程、線程的概念,這些概念都在有了操做系統以後纔有的概念。

由於沒有操做系統,也沒有內存空間的概念。不像是咱們如今所說的程序(Program),不一樣的程序有本身獨立的內存地址空間。咱們這裏,內存(memory)對於不一樣的 Program 來講是 shared。另外,須要注意的是 Program 能夠用來講明各類程序,無論你是操做系統內核,仍是應用程序,都適用。

剛剛咱們說了,sequential consistency 雖然是針對併發編程的領域提出的,但實際上它是分佈式領域的概念,特別是分佈式存儲系統。《Distributed system: Principles and Paradigms》(做者Andrew S.Tanenbaum, Maarten Van Steen)這本書中,做者稍微修改了一下 Lomport 的定義,讓這個定義更貼近分佈式領域中的概念,咱們來看一下做者是怎麼改的:

The result of any execution is the same as if the (read and write) operations by all processes on the data store were executed in some sequential order and the operations of-each individual process appear in this sequence in the order specified by its program.

做者把 processor 換成了 process,而且加了 on the data store 這個限定,在 Lomport 沒有這個限定,其實默認指的是 memory(內存)。Process 就是指進程。以 zookeeper 爲例,就是指訪問 zookeeper 的應用進程。program 也不是那麼底層概念,也是基於操做系統的應用程序了。

好了,下面我該揭曉我上面賣的 2 個關子了。在 Lomport 的論文中,給出了一個小例子,以下:

process 1
        a := 1;
        if b = 0 then critical section:
                a := 0
            else ... fi

    process 2
        b := 1;
        if a = 0 then critical section:
                b := 0
            else ... fi
複製代碼

Lomport 在論文中說,若是一種多核處理知足 sequential consistency 的條件,那麼 最多隻有一個程序可以進入 critical section。在論文中,Lomport 老爺子並無解釋爲何最多隻有一個程序可以進入 critical section。而是把這個證實留給了論文的讀者,就像咱們常見的教科書中的課後習題同樣,留給的讀者。

Lomport 老爺子應該是認爲這個證實太簡單了,不該該花費它的筆墨來證實它。sequential consistency 這篇論文只有不到 2 頁 A4 紙,是我見過的最短的論文。這是 Lomport 老爺子一項的作事風格,Lomport 的 Paxos 論文中,有不少細節,都是一筆帶過的,給讀者留下無盡的遐想(瞎想)。 假設如今咱們已經證實這個是正確的(雖然我也沒去證實一下,論文給出 2 個參考文獻,用來證實這個),這個例子說明了什麼那?你也許注意到了,這個例子沒有用到任何鎖,可是它實現了 critical section,critical section 是一種多線程 synchronization 機制。若是多處理器是 sequential consistency 的,那麼你寫的併發程序"自然就是正確的"。

可是處理器的設計者爲了最求性能,將保證程序正確的任務丟給程序開發者。只在硬件級別提供了一些 fence、cas 等指令,基於這些指令操做內核和語言基礎庫實現了各類 synchronization 機制,用來保證操做系統的正確性和應用程序的正確性。程序員必須當心謹慎的使用線程和這些 synchronization 機制,不然就會出各類意想不到的問題。

若是你沒有 debug 一個多線程 bug 連續加班 2 天,那說明你是大神。這些指令都是具備更高一致性級別,也就是 linearizability(關於 linearizability 能夠參看個人另一篇文章《線性一致性是併發控制的基礎》,雖然一致性級別高,但只是個別指令的,處理器總體只是實現了比 sequential consistency 低不少的一致性級別。因此實現難度大大的下降了。

雖然 Lomport 老爺子的 sequential consistency 的概念在 concurrent programming 領域中尚未實際意義,可是卻給咱們指出了程序員的天堂在哪裏。在程序員的天堂裏,沒有多(車)線(來)程(車)編(往)程,只用寫程序就行。你面試的時候不會再有人問你多線程編程,不會再問你各類鎖。

在分佈式領域中,sequential consistency 更實際一些。zookeeper 就實現了 sequential consistency。同理,這應該也是能夠證實的,可是目前還沒發現有 zookeeper 社區有任何論文來證實這個。若是你已經明白上面解釋的定義,你能夠想清楚 zookeeper 是 sequential consistency。歡迎你們一塊兒來討論。

ZK的一致性

實際上,ZooKeeper 的一致性更復雜一些,ZooKeeper 的讀操做是 sequential consistency 的,ZooKeeper 的寫操做是 linearizability 的.(關於 linearizability 能夠參看個人另一篇文章《線性一致性是併發控制的基礎》

關於這個說法,ZooKeeper 的官方文檔中沒有寫出來,可是在社區的郵件組有詳細的討論。另外在這篇關於 ZooKeeper 的論文《Modular Composition of Coordination Services》 中也有提到這個觀點(這篇論文不是 ZooKeeper 的主流論文,可是全面分析了 ZooKeeper 的特性,以及 ZooKeeper 跨機房方案,餓了麼的 ZooKeeper 異地多活改造也參考了這篇論文中的一些觀點)。咱們能夠這麼理解 ZooKeeper,從總體(read 操做 +write 操做)上來講是 sequential consistency,寫操做實現了 Linearizability。

經過簡單的推理,咱們能夠得出 Lomport 論文中的小例子,在 ZooKeeper 中也是成立的。咱們能夠這樣實現分佈式鎖。但 ZooKeeper 官方推薦的分佈式實現方法並無採用這個方式來實現,而是利用了 Zookeeper 的 Linearizability 特性實現了分佈式鎖(關於 ZooKeeper 官方是如何實現分佈式鎖的,請參考個人這篇文章《ZooKeeper 實現分佈式鎖和選主》)。

爲何ZK要實現 sequential consistency?

ZooKeeper 最核心的功能是用來作 coordination service,也就是用來作分佈式鎖服務,在分佈式的環境下,ZooKeeper 自己怎麼作到"自然正確"?沒有其餘的 synchronization 機制保證 ZooKeeper 是正確的,因此只要 zk 實現了 sc,那他自身就能夠保證正確性,從而對外提供鎖服務。


參考文檔

  1. zookeeper.apache.org/doc/r3.4.9/…
  2. blog.csdn.net/cadem/artic…
  3. comments.gmane.org/gmane.comp.…
  4. blog.csdn.net/cadem/artic…
相關文章
相關標籤/搜索