《云计算技术系列丛书 云原生分布式存储基石: etcd深入解析》—1.2一致性
1.2 一致性
在阐述一致性模型和一致性协议之前,我们先来了解下什么是一致性。分布式存储系统通常会通过维护多个副本来进行容错,以提高系统的可用性。这就引出了分布式存储系统的核心问题—如何保证多个副本的一致性?
“一致性”这个中文术语在计算机的不同领域具有不同的含义,不同的含义所对应的英文术语也是不一样的,例如,Coherence、Consensus和Consistency等。就这三个术语而言,简单来说,它们之间存在的区别具体如下:
Coherence这个单词只在Cache Coherence场景下出现过,其所关注的是多核共享内存的CPU架构下,各个核的Cache上的数据应如何保持一致。
Consensus是共识,它强调的是多个提议者就某件事情达成共识,其所关注的是达成共识的过程,例如Paxos协议、Raft选举等。Consensus属于replication protocol的范畴。
Consistency表达的含义相对复杂一些,广义上说,它描述了系统本身的不变量的维护程度对上层业务客户端的影响,以及该系统的并发状态会向客户端暴露什么样的异常行为。CAP、ACID中的C都有这层意思。
本书将要重点讨论的分布式系统中的一致性问题,属于上文中提到的Consensus和Consistency范畴。分布式系统的一致性是一个具备容错能力的分布式系统需要解决的基本问题。通俗地讲,一致性就是不同的副本服务器认可同一份数据。一旦这些服务器对某份数据达成了一致,那么该决定便是最终的决定,且未来也无法推翻。
一致性与结果的正确性没有关系,而是系统对外呈现的状态是否一致(统一)。例如,所有节点都达成一个错误的共识也是一致性的一种表现。
一致性协议就是用来解决一致性问题的,它能使得一组机器像一个整体一样工作,即使其中的一些机器发生了错误也能正常工作。正因为如此,一致性协议在大规模分布式系统中扮演着关键角色。
同时,一致性协议也是分布式计算领域的一个重要的研究课题,对它的研究可以追溯到20世纪80年代,一致性协议衍生出了很多算法。衡量一致性算法的标准具体如下。
可终止性:非失败进程在有限的时间内能够做出决定,等价于liveness。
一致性:所有的进程必须对最终的决定达成一致,等价于safety。
合法性:算法做出的决定值必须在其他进程(客户端)的期望值范围之内。即客户端请求回答“是”或“否”时,不能返回“不确定”。
一致性协议是在复制状态机(Replicated State Machines,RSM)的背景下提出来的,通常也应用于具有复制状态机语义的场景。在了解复制状态机之前,让我们先简单了解下一致性模型。
1.2.1 一致性模型
一致性问题一直以来都是分布式系统的痛点,因为很多场景都要求一致性,但并不是所有的系统都要求是强一致的。强一致需要极高的成本,我们需要根据系统的容忍度适当放宽一致性的要求。
在很多人看来,银行间的转账应该是强一致的,但是如果仔细分析一下就会发现,小王向小张转账1000元,小王的账户扣除了1000元,此时小张并不一定会同步收到1000元,可能会存在一个不一致的时间窗口。也就是小王的账户中扣除了1000元,小张还没收到1000元。另外一个常见的例子,12306网站上买票的功能也未必是强一致的,如果你在12306上发现某车次的票还剩余10张,发起请求订了一张票,系统返回的信息可能是“正在排队,剩余10张票,现在有15人在购买”,而不是购买成功或失败的结果,很可能你在收到上述信息之后,不得不去查询未完成订单,以进一步确认订票情况。如果有人退了一张票,通常这张票也不会立即返回到票池中。很明显这里也存在不一致的时间窗口。
本节将要重点讨论分布式系统的一致性模型。我们知道,分布式系统中网络分区在任何时刻、任何地点都有可能正在或即将发生。交换机、网卡、主机硬件、操作系统、磁盘、虚拟化层和语言运行时间(更不用说程序语义本身)都会延误、丢弃、复制或重新排序我们的消息。在一个不确定的世界里,我们肯定都是希望自己的软件能够按照确定的规则运行。
那么,很显然我们需要直观的正确性。做正确的事情!那么究竟什么是正确的呢?我们又该如何描述它呢?
正确性
我们有很多种方式来表达一个算法的抽象行为,比如前文中介绍的状态机模型—“一个系统是由状态以及改变这些状态的操作组成的”,随着系统的运行,它会通过一些操作历史从一个状态转移到另一个状态。
如果我们的状态是一个变量,状态上的操作可能是写入和读取该变量,那么,如下这个简单的Ruby程序将会多次写入和读取一个变量,并将其打印到屏幕上,以说明读取的内容。示例代码如下:
x = "a"; puts x; puts x
x = "b"; puts x
x = "c"
x = "d"; puts x
在上述示例代码里,我们已经有了这个程序正确性的直观模型:它应该打印“aabd”。为什么?因为每个陈述都是按顺序发生的。首先写入一个值a,然后是读取两次值a,再写入值b,然后读取值b等。上述寄存器系统读写输出具体如图1-1所示。
图1-1 寄存器系统读写输出示例
我们将这种一个变量携带一个值的系统称为寄存器。一旦我们将一个变量(寄存器)设置为某个值,该值就会立刻生效,直到我们再次更改该值,即读取变量应该返回最近写入的值。
从开始编写程序的第一天起,这种模式就已经深深地印刻在了我们的头脑之中,然而这并非变量唯一的工作方式。事实上,一个变量可以返回任何一个读取的值:a、d或the moon。如果发生这种情况,则认为系统是不正确的,因为这些操作与我们的变量应该如何工作的模型不一致。这也暗示了系统正确性的定义:在给定了与操作和状态相关的一些规则的情况下,系统中的操作历史应该总是遵循这些规则。我们称这些规则为一致性模型。
更正式的说法是,一致性模型是所有允许的操作历史的集合。如果运行一个程序,它经历了“允许操作集”中的一系列操作,那么任意一次执行都是一致的。如果程序偶尔发生故障并且出现了不是一致性模型中的历史操作,那么我们就说历史记录是不一致的。如果每个可能的执行都落入允许的集合中,则系统满足该一致性模型。我们希望真正的系统能够满足“直观正确”的一致性模型,以便编写可预测的程序。
- 点赞
- 收藏
- 关注作者
评论(0)