不理解Zookeeper一致性原理,谈何异地多活改造

转载:http://developer.51cto.com/art/201805/574334.htm

Zookeeper 是最终一致性的,由于多副本,以及保证大多数成功的 Zab 协议,当一个客户端进程写入一个新值,另一个客户端进程不能保证马上就会读到,但能保证最终会读到这个值。

Zookeeper 的 Zab 协议类似于 Paxos 协议,并且提供了强一致性。

每当听到这两种说法,我都想纠正一下。Zookeeper 是顺序一致性(Sequential Consistency)的,但解释起来比较复杂,下面和大家一起讨论下我的看法。

什么是 Sequetial Consistency

从 Zookeeper 的文档中我们可以看到,里面明确写出它的一致性是 Sequential Consistency。

那么,什么是 Sequential Consistency 呢?

Sequential Consistency 是 Lamport 在 1979 年首次提出的。论文中定义,当满足下面这个条件时就是 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.

这段英文定义很晦涩,这是 Lamport 大神的一贯风格,严谨但晦涩,Paxos 协议也是如此。

我第一次看到时的感觉是:“这是什么鬼?”为啥每个英文单词我都认识,但就是不知道他在说什么?

我们先看看这篇论文的标题和定义中出现的一个关键词,来说明一下 Sequential Consistency 的应用范围。

论文的标题和定义中包含“multiprocessor”这个词,multiprocessor 是多核处理器的意思。

从这个关键字来看,Sequential Consistency 是用来定义多核处理器和跑在多核处理器上的程序的一个特性。

Lamport 这篇论文的标题可以翻译成:“如何让具有多核处理器的计算机正确执行多进程程序”。

也就是说如果一个多核处理器具有 Sequential Consistency 的特性,这个多核处理器就可以正确运行,后面我会解释这个正确运行是什么意思(也就是本文后面讲到的 Sequential Consistency 的作用)。

从这个标题我们还可以看出,Sequential Consistency 应该是个并发编程(Concurrent Programming)领域的概念。

但我们现在常常在分布式系统领域讨论 Sequential Consistency,比如本文主要讨论的 Zookeeper(Zookeeper 很明显是一个分布式系统)的一致性。

实际上,多核处理器上运行的多个程序,其实也是一种分布式系统(Lamport在他的这篇 Time,Clocks,and the Ordering of Events in a Distributed System 分布式系统的开山之作中也阐述了这个观点)。

所以虽然 Sequential Consistency 最早在并发编程中提出,但是它可以应用在分布式系统中,比如本文讨论的 Zookeeper 这种分布式存储系统。

另外一个比较重要的 Linearizability(线性一致性),也是在并发编程中最早提出的,目前也被广泛应用在分布式系统领域中。

接下来我们尝试翻译一下上文那段晦涩的定义,做这段翻译让我找到了上学时做阅读理解的感觉。

我先不直接翻译,因为就算我把它翻译成中文,估计很多人还是会有“为啥每个中文字我都懂,还是不知道在说什么”的感觉。

首先,我来解释一下个别的词。首先,“any execution”是什么意思?你有多个程序(Program)在多核处理器上运行,例如你有两个程序。

第一个程序叫 P1,它的代码如下:

 
  1. P1_write(x); 
  2. P1_read(y); 

第二个程序叫 P2,代码如下:

 
  1. P2_write(u); 
  2. P2_read(v); 

从理论上来讲,两个程序运行在两个独立处理器的核上,有多少种执行的可能?我列举其中几种来举例说明。

第一种:

 
  1. P1---write(x)--------read(y)-------- 
  2. P2-----------write(u)-------read(v)- 

第二种:

 
  1. P1----------write(x)-read(y)-------- 
  2. P2--write(u)----------------read(v)- 

第三种:

 
  1. P1---read(y)----------write(x)------ 
  2. P2-----------write(u)---------read(v)- 

我们有 24 种可能的执行顺序,也就是这四个操作任意的排列组合,也就是4!=24。

类似第一种和第二种这样的可能性很好理解。为什么会出现像第三种这样的可能的执行呢?

那是因为就算在同一个程序中,由于处理会有多级的缓存,以及处理器中 coherence 的存在,虽然你的程序中是先 write 后 read,在内存中真正生效的顺序,也有可能是先 read 后 write。

其实还会出现类似下面这样的执行,两个操作在两个处理器上同时执行。

 
  1. P1--write(x)-read(y)-------- 
  2. P2--write(u)--------read(v)- 

如果加上同时运行的这种情况,那就有更多种可能性。我的算术不太好,这里就不继续算下去了,因为到底有多少个不重要,重要的是要知道有很多种可能性。

那么定义中的“any execution”,就是指任意一种可能的执行,在定义中也可以理解为所有的这些可能的执行。

接着我们再来解释一个词——“sequential order”。什么叫 sequential order?我们来翻一下英语词典(感觉更像在做阅读理解了)。

sequential:连续的;相继的;有顺序的

order:命令;顺序;规则;[贸易] 定单

sequential order——有顺序的顺序,这又是什么鬼?

其实 sequential 是有一个接一个的意思,在处理器的这种上下文中,sequential 就是指操作(operartion)一个接一个地执行,也就是顺序执行,并且没有重叠。

order 是指经过一定的调整,让某样东西按照一定的规则变得有序。比如,在算法中的排序算法就是 ordering,就是让数组这个东西按照从大到小或从小到大的规则变得有序。

那么 sequential order 就是指让操作(operation)按照一个接一个这样的规则排列,并且没有重叠。

再说回上文的例子,如果把四个操作,按一个接一个的规则排列,这时就可以得到 4!的排列组合个可能的排列(order),同样的,有多少个不重要。

比如:

 
  1. P1_write(x);P1_read(y);P2_write(u);P2_read(v); 
  2. P1_read(y);P1_write(x);P2_write(u);P2:read(v); 
  3. P2_write(u);P2_read(v);P1_read(y);P1:write(x); 

我这里只列举其中三个,其他的大家可以自己排一下。

重点来了,其实 sequential order 就是让这四个操作按照一个接一个的顺序执行,并且没有重叠。

注意这个排列不是真实的执行,真实的执行是 any execution,这里说的是逻辑上的假设,也就是为什么定义有一个 as if。

做了这么多的铺垫,下面我们开始翻译定义中的第一句话:

任意一种可能的执行效果,与所有的处理器上的某一种操作按照顺序排列执行的效果是一样的。

注意,some 在这里是指“某一种”的意思,不是一些,因为 order 是单数(真的是在做阅读理解)。

这句话的意思就是说,一个处理器要满足这个条件,就只能允许满足这个条件的那些可能的执行存在,其他不满足的可能的执行都不会出现。

从第一句话中我们可以看出,一种多核处理器要想满足 Sequential Consistency,那么多个程序在多个核运行效果“等同”于在一个核上顺序执行所有操作的效果是差不多的。

如果这样的话,其实多核的威力基本就消失了。所以无论是从 Lamport 写这篇论文的 1979 年,还是现在,没有任何一个现实的多核处理器实现了 Sequential Consistency。

那么,为什么 Lamport 大神提出这样一个不现实的概念呢?(要注意 Lamport 写这篇论文时,并没有把它引申到分布式系统领域,就是针对多核处理器、并发编程领域提出的)我们稍后再论述。

这里还要注意的一点是,在我的翻译里用了“效果”一词,但实际上英文原文中用的是“result(结果)”一词。那效果和结果有什么区别吗?我们解释一下什么叫执行结果。

不管是任何真实的执行,还是某种经过顺序排序后的假设执行,程序会产生一定的结果,比如 print 出来的结果(result)。实际上定义中说的是结果一样。

如果定义中用“效果”的话,那这个定义就只是一个定性的定义,如果用“结果”的话,那这个定义就是一个定量的定义。定量的,也就是说可以通过数学证明的。

从这点我们可以看出,大神就是不一样,任何理论都是可以通过数学证明为正确的。本文后面还会提到证明的事情,我们这里再卖个关子。

到这里,关于定义的第一句,更准确的翻译就是:

任意一种可能的执行的结果,与某一种所有的处理器上的操作按照顺序排列执行的结果是一样的。

这里我们还要注意一点,结果一样就意味着,如果有人真的要实现一种 Sequential Consistency 的多核处理器的话,因为要保证结果一样,所以他是有一定的空间来优化,而不会完全是一个按顺序执行的效果。

但是估计这种优化也是非常有限的。好了,终于把最难的第一句话解释完了,大家可以松口气,第二句就非常简单了。

我们还是先解释第二句中出现的一个词——“sequence”。刚刚解释过的 sequential order 是顺序排序(就是按一个接一个排序)。

其实这是一个动作,动作会产生结果,它的结果产生了一个操作(operation)的队列。第二句中出现的 sequence 就是指这个操作(operation)的队列。

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

并且每个独立的处理器的操作,都会按照程序指定的顺序出现在操作队列中。

也就是说如果程序里是先write(x);后read(y);,那么只有满足这个顺序的操作队列是符合条件的。

这样,我们刚刚说的很多可能的执行就少了很多,这里我也就不计算少了多少,还是那句话,数量不重要,反正是有,而且变少了。

那么第二句话意味着什么?意味着如果一个多核处理器实现了 Sequential Consistency,那这种多核处理器基本上就告别自(缓)行(存)车了。

这里我还要继续卖关子,连缓存这种最有效提高处理器性能的优化都没了,大神为什么要提出这个概念?

好了,到这里我们可以把两句翻译合起来,完整看一下:

任意一种可能的执行的结果,与某一种所有的处理器上的操作按照顺序排列执行的结果是一样的,并且每个独立的处理器的操作,都会按照程序指定的顺序出现在操作队列中。 

从这个定义中,我们可以看出,此概念的核心就是 sequential order,这也就是为什么 Lamport 老爷子把这种一致性模型称之为 Sequential Consistency。

可以说这个命名是非常贴切的,不知道这种贴切对于以英语为母语的人来说是不是更好理解一些,应该不会出现“顺序的顺序是什么鬼”这种情况。如果你看完本文,也觉得 sequential 很贴切的话,那就说明我讲清楚了。

接下来我们举个具体的例子,再来说明一下:

 
  1. execution A  
  2. P0 writex=1------------------------------- 
  3. P1 -------write x=2---------------------- 
  4. P2 -----------------read x==1--read x==2 
  5. P3 -----------------read x==1--read x==2 
  6. 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 
  7. execution B  
  8. P0 write=1-------------------------------  
  9. P1 -------write x=2----------------------  
  10. P2 -----------------read x==2--read x==1  
  11. P3 -----------------read x==2--read x==1 
  12. 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 
  13. execution C 
  14. P0 write=1-------------------------------  
  15. P1 -------write x=2----------------------  
  16. P2 -----------------read x==1--read x==2  
  17. P3 -----------------read x==2--read x==1   
  18. sequetial order: 你找不出一个符合定义中2个条件的一种order。 

sequetial order: 你找不出一个符合定义中2个条件的一种order。

所以说如果一个多核处理器只允许 execution A 和 B 出现,不允许 C 出现,那么这个多核处理器就是 Sequetial Consistency 的;如果它允许 C 出现,那它就不是 Sequetial Consistency。

到这里,我们已经完整地解释完什么是 Sequetial Consistency。但是,细心的朋友可能会问,如果你的 program 是多线程的程序怎么办?那我们再把定义中最后的一个细节解释一下:program 这个词。

program 是指可以直接运行在处理器上的指令序列。这个并不是 program 的严格定义,但是我要指出的是这个 program 是在操作系统都没有的远古时代就存在的概念,在上文的定义中 prgram 就是指那个时代的 program。

这个 program 里没有进程、线程的概念,这些概念都是在有了操作系统之后才出现的。因为没有操作系统,也没有内存空间的概念。

不像我们现在所说的程序(program),不同的程序有自己独立的内存地址空间。我们这里,内存(memory)对于不同的 program 来说是 shared。

另外,需要注意的是 program 可以用来说明各种程序,不管你是操作系统内核,还是应用程序,都适用。

Sequential Consistency 是分布式领域的概念

刚刚我们说了,Sequential Consistency 虽然是针对并发编程领域提出的,但实际上它是分布式领域的概念,特别是分布式存储系统。

在 Distributed system:Principles and Paradigms (作者Andrew S.Tanenbaum, Maarten Van Steen),作者稍微修改了一下 Lamport 的定义,让这个定义更贴近分布式领域中的概念。

我们来看一下作者是怎么改的:

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 这个限定,在 Lamport 的定义里没有这个限定,其实默认指的是 memory(内存)。

process 就是指进程,以 Zookeeper 为例,就是指访问 Zookeeper 的应用进程。program 也不是那么底层的概念,也是基于操作系统的应用程序了。

Sequential Consistency 的作用

好了,下面该揭晓我上面卖的两个关子了。在 Lamport 的论文中,给出了一个小例子,如下:

 
  1. process 1 
  2.     a := 1; 
  3.     if b = 0 then critical section: 
  4.             a := 0 
  5.         else ... fi 
  6. process 2 
  7.     b := 1; 
  8.     if a = 0 then critical section: 
  9.            b := 0 
  10.         else ... fi 

Lamport 在论文中说,如果一种多核处理满足 Sequential Consistency 的条件,那么最多只有一个程序能够进入 critical section。

在论文中,Lamport 老爷子并没有解释为什么最多只有一个程序能够进入 critical section。

而是把这个证明留给了论文的读者,就像我们常见的教科书中的课后习题一样。

Lamport 老爷子应该是认为这个证明太简单了,不需要花费笔墨来证明它。Sequential Consistency 这篇论文只有不到两页 A4 纸,是我见过的最短的论文。

这是 Lamport 一贯的做事风格,他的 Paxos 论文中,有很多细节都是一笔带过的,给读者留下无尽的遐想(瞎想)。

假设现在我们已经证明这个是正确的(虽然我也没去证明,论文给出了两个参考文献用于证明),那这个例子说明了什么呢?

大家也许注意到了,这个例子没有用到任何锁,但它实现了 critical section,critical section 是一种多线程 synchronization 机制。

如果多核处理器是 Sequential Consistency 的,那么你写的并发程序“天然就是正确的”。

但是处理器的设计者为了追求性能,将保证程序正确的任务丢给程序开发者。

只在硬件级别提供了一些 fence、cas 等指令,基于这些指令操作内核和语言基础库实现了各种 synchronization 机制,用来保证操作系统的正确性和应用程序的正确性。

程序员必须小心谨慎地使用线程和这些 synchronization 机制,否则就会出各种意想不到的问题。如果你没有 debug 一个多线程 Bug 连续加班两天,那说明你是大神。

这些指令都是具有更高一致性级别,也就是 linearizability,虽然一致性级别高,但只是个别指令的,处理器整体只是实现了比 Sequential Consistency 低很多的一致性级别。所以实现难度大大降低了。

虽然 Lamport 老爷子的 Sequential Consistency 概念在 Concurrent Programming 领域中还没有实际意义,但却给我们指出了程序员的天堂在哪里。

在程序员的天堂里,没有多(车)线(来)程(车)编(往)程,只要写程序就行。你面试的时候不会再有人问你多线程编程,不会再问你各种锁。

在分布式领域中,Sequential Consistency 更实际一些。Zookeeper 就实现了 Sequential Consistency。

同理,这应该也是可以证明的,但目前还没发现有 Zookeeper 社区有任何论文来证明这个。

如果你已经明白上面解释的定义,就可以想清楚 Zookeeper 是 Sequential Consistency,欢迎大家一起来探讨。

为何 Zookeeper 要实现 Sequential Consistency

实际上,Zookeeper 的一致性更复杂一些,Zookeeper 的读操作是 Sequential Consistency 的,Zookeeper 的写操作是 linearizability 的。

关于这个说法,Zookeeper 的官方文档中没有写出来,但在社区的邮件组有详细的讨论。

另外,在 Modular Composition of Coordination Services 这篇关于 Zookeeper 的论文中也有提到这个观点(这篇论文不是 Zookeeper 的主流论文,但全面分析了 Zookeeper 的特性,以及 Zookeeper 跨机房方案,饿了么的 Zookeeper 异地多活改造也参考了这篇论文中的一些观点)。

我们可以这么理解 Zookeeper,从整体(read 操作+write 操作)上来说是 Sequential Consistency,写操作实现了 linearizability。

通过简单的推理,我们可以得出 Lamport 论文中的小例子,在 Zookeeper 中也是成立的。我们可以这样实现分布式锁。

但 Zookeeper 官方推荐的分布式实现方法并没有采用这个方式,而是利用了 Zookeeper 的 linearizability 特性实现了分布式锁。

为什么 Zookeeper 要实现 Sequential Consistency?Zookeeper 最核心的功能是用来做 coordination service,也就是用来做分布式锁服务,在分布式的环境下,Zookeeper 本身怎么做到“天然正确”?

没有其他的 synchronization 机制保证 Zookeeper 是正确的,所以只要 Zookeeper 实现了 Sequential Consistency,那它自身就可以保证正确性,从而对外提供锁服务。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/463532.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

作诗一首

只恐花尽老相催,为爱鸬鹚雨里飞。 大珠小珠落玉盘,今日征行何岁归。 今日,在此作诗一首,来抒发一下老周我对心爱的小邢的思念。虽内心迷茫,却仍然坚守;虽征邢之难,偏向邢征行。只为相信曾经——…

一道C#类型转换的思考题

c#中,short s1 1; s1 s1 1;有什么错? (真有问题)short s1 1; s1 1;有什么错?(没问题)分析:留给你们。转载于:https://www.cnblogs.com/legecoding/archive/2012/07/23/2604991.html

程序员修炼之道--从小工到专家(一)

这个假期里看了一下这本书,乍一看名字,觉得这就是比较鸡汤类的书吧,但是在看了一部分之后,忽然感觉到,以前的自己真的是很无知,一直就没有认识到做一个真实的项目的困难性,也根本就没有真正的认…

2018年文章汇总

Android ANR 实例分析Linux kernel计算某段代码运行时间Linux Kernel 发展和内核特点C/C函数指针与指针函数(二)老王带你理解算法复杂度O(1),O(N),O(N^2)Android NDK Tombstone/Crash 分析堆和栈的区别(转过无数次的文章)C语言scanf-周末杂想C语言-scanf…

C++11:右值引用和转移赋值

1、左值与右值的区别: 左值:能别赋值的值;能取到地址的值,用&能取到地址,具有持久性; 右值:临时变量;不能用&取到地址;只是字面常量值 2、右值引用 一般用的…

[备忘]Visual Studio常用小技巧

1. 怎样调整代码排版的格式? 选择:编辑—>高级—>设置文档的格式 或 编辑—>高级—>设置选中代码的格式。 格式化cs代码:Ctrlkf 格式化aspx代码:Ctrlkd 2. 怎样跳转到指定的某一行? 两种方法&#…

Angular CLI的简单使用(1)

参考地址: https://v2.angular.cn/docs/ts/latest/cli-quickstart.html Angular CLI是一个命令行界面工具,它可以创建项目、添加文件以及执行一大堆开发任务,比如测试、打包和发布。 1. 请先在终端/控制台窗口中运行命令 node -v 和 npm -v, 如下图,没有…

在没有数据集的情况下使用数据表

使用数据适配器填充数据表 View Code using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Data; using System.Data.SqlClient;namespace PopDataTable {class Program{static void Main(string[] args){string connString…

2019年文章汇总

要不要去培训机构深造一下?我一个37岁的程序员朋友Linux内核中的GPIO系统整理的免费资料,帮你年后跳槽C语言,字符串指针做函数参数书籍推荐过年回家抢票攻略C语言,函数不可返回指向栈内存的指针你还会写这段C51程序吗?…

一种简单的LRU cache设计 C++

最近在工作中需要用到LRU cache用作缓存来提高性能,经过查阅各种资料,了解了其运行的机制,如下: LRU cache可以用于在内存中保持当前的热点数据,下面实现一个有大小限制的lru cache,相关如下: …

开博了,喜欢这里的气氛

开博了,喜欢这里的气氛 以后经常写东西 学PHP不久,很多东西需要学习 我的站点 www.zhibowang.cn www.xzld.com转载于:https://www.cnblogs.com/gxphp/archive/2009/04/05/1429819.html

bsgs整理

目录 bsgs问题 或 poj2417:概述代码exbsgs鸣谢 \(gzy gzy gzy\)bsgs问题 或 poj2417: 给定质数\(p\),给定\(a\),\(b\),\((a,p)1\) 求出最小的整数x,使得\(a^{x}≡b(mod p)\) 概述 由费马小定理可以知道\(a^{xp-1}≡a^{x}≡b(mod p…

两个指针变量可以相减

两个指针变量可以相减转载于:https://www.cnblogs.com/LoveFishC/archive/2012/07/23/3845529.html

linux下修改max_user_processes和open_file的最大值

从事分布式服务器开发工作的都会遇到,linux下open_file的值默认是1024;max user processes的值默认是4096,在实际用于中,这两个值严重不足,常常需要调整这两个值。默认配置如下: 可以通过以下两种方式修改&…

Silverlight 全屏显示

privatevoidFullScreenButton_Click(objectsender, RoutedEventArgs e) { Application.Current.Host.Content.IsFullScreen Application.Current.Host.Content.IsFullScreen?false:true; }转载于:https://www.cnblogs.com/star250/archi…

IP SLA的路径控制

一、定义SLA 服务水平协议(简称:SLA,全称:service level agreement)是在一定开销下为保障服务的性能和可靠性,服务提供商与用户间定义的一种双方认可的协定。通常这个开销是驱动提供服务质量的主要因素。简…

创业动力衰减 中国面临危险挑战

近日,在翻阅1月份出的一书《赢道:成功创业者的28条戒律》时,对目前国内的创业形势产生了近乎激动的想法。从目前的创业情况看,我认为,中国80年代青年的创业动力正在衰减,对照2005年—2007年间各种创业沙龙如…

【linux】linux系统中常见配置文件及目录说明

1.配置文件/proc/sys/fs/file-nr 里文件里显示三个数字 [rootlocalhost logs]# cat /proc/sys/fs/file-nr 2112 0 2100000已分配文件句柄的数目 已使用文件句柄的数目 文件句柄的最大数目 上面第二项的值为0表示分配的文件…

6-14 数据库高级

--先通过设计器手动添加,然后通过代码来添加--手动增加约束--手动删除一列(删除EmpAddress列) alter table Employees drop column EmpAddress go--手动增加一列(增加一列EmpAddr varchar(1000)) alter table Employees add EmpAddr varchar(1000)--手动修改一下Emp…

ptmalloc,tcmalloc和jemalloc内存分配策略研究

转载:https://cloud.tencent.com/developer/article/1173720 操作系统内存布局 各种malloc的内存分配管理方式离不开操作系统的内存布局策略。 32位经典内存布局 32位系统下经典内存布局如上,程序起始的1GB地址为内核空间,接下来是向下增长…