如何理解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…
相关文章
相关标签/搜索