标签:根据 tag ash split cap 范围 memory oracl 通信
如果你想引用 CAP 作为一个定理(而不是一个模糊的,用来做数据库市场营销的概念),你需要用非常精确的定义。数学要求精确。只有当你的用词和定理的证明中的定义是一样的时候,这个证明才有意义。CAP 的证明用的是非常具体的定义。
还有就是注意到 CAP 并没有描述任意一个老的系统,而是一个非常特殊的系统:
如果你的用词是符合 CAP 证明中的精确定义的,那么它对你来说是适用的。但是如果你的一致性还有可用性是有其他意思的,那么你不能期待 CAP 对你还是适用的。当然,这并不意味着你通过重新定义一些词汇就可以做到一些不可能的事情!这只是说你不能靠 CAP 来给你提供指导方向,而且你不能通过 CAP 来为你的观点来辩解。
如果 CAP 定理不适用,那么这就意味着你必须自己来考虑取舍。你必须根据你自己对一致性还有可用性的定义来思考这些属性,而且你能证明自己的定理就更好了。但是请不要称它为 CAP 定理,因为这个名字已经被用了。
如果你对可线性化不是很熟悉(也就是 CAP 中的一致性),那么让我来简短地解释一下。正式的定义不是特别直观,但是关键的思想用非正式的描述就是:
如果B操作在成功完成 A 操作之后,那么整个系统对 B 操作来说必须表现为 A 操作已经完成了或者更新的状态。
为了可以解释的更清楚一些,让我们来看一个例子。在这个例子中的系统并不是可线性化的。
看下面这个图(我还没有发行的书的预览):
这张图展示了 Alice 还有 Bob, 他们在同一个房间,都在用他们的手机查询 2014 年世界杯的决赛结果。就在最终结果刚发布之后,Alice 刷新了页面,看到了宣布冠军的消息,而且很兴奋地告诉了 Bob。Bob 马上也重新加载了他手机上的页面,但是他的请求被送到了一个数据库的拷贝,还没有拿到最新的数据,结果他的手机上显示决赛还正在进行。
如果 Alice 和 Bob 同时刷新,拿到了不一样的结果,并不会太让人意外。因为他们不知道具体服务器到底是先处理了他们中哪一个请求。但是Bob知道他刷新页面是在 Alice 告诉了他最终结果之后的。所以他预期他查询的结果一定比 Alice 的更新。事实是,他却拿到了旧的结果。这就违反了可线性化。
只有 Bob 通过另外一个沟通渠道从 Alice 那里知道了结果, Bob 才能知道他的请求一定在 Alice 之后。如果 Bob 没有从 Alice 那里听到比赛已经结束了,他就不会知道他看到的结果是旧的。
如果你在建一个数据库,你不知道用户们会有什么另外的沟通渠道。所以,如果你想提供可线性化(CAP 的一致性),你就需要让你的数据库看起来就好像只有一个拷贝,虽然实际上可能有多个备份在多个地方。
这是一个非常昂贵的属性,因为它要求你做很多协调工作。甚至你电脑上的 CPU 都不提供本地内存的可线性化访问! 在现代的 CPU 上,你需要用 memory barrier 指令来达到可线性化访问。甚至测试一个系统是不是可线性化的也是很困难的。
让我们来简短的讨论一下为什么在网络分区的情况下,我们要放弃可用性和一致性中的一个。
举个例子,你的数据库有两个拷贝在两个不同的数据中心。具体怎么做备份并不重要,可以是 single-master,或者多个 leader,或者基于 quorum 的备份(Dynamo 使用的方式)。要求是当数据被写到一个数据中心的时候,他也一定要被写到另一个数据中心。假设 client 只连接到其中一个数据中心,而且连接两个数据中心的网络故障了。
那么现在假设网络中断了,这就是我们所说的网络分区的意思。接下来怎么样呢?
显然你有两个选择:
(而这个,其实就是 CAP 定理的证明。这就是全部了。这里的例子用到了两个数据中心,但是对于一个数据中心内的网络故障也是同样适用的。我只是觉得用两个数据中心这样更容易考虑这个问题。)
注意到上面第二点,就算它违反了 CAP 的可用性,但我们还是在成功地处理着请求。所以当一个系统选择了可线性化(也就是说不是 CAP 可用的),这并不一定意味着网络分区一定会造成应用停运。如果你可以把用户的流量转移到leader数据库,那么用户根本就不会注意到任何问题。
实际应用中的可用性和 CAP 可用性并不相同。你应用的可用性多数是通过 SLA 来衡量的(比如 99.9% 的正确的请求一定要在一秒钟之内返回成功),但是一个系统无论是否满足 CAP 可用性其实都可以满足这样的 SLA。
实际操作中,跨多个数据中心的系统经常是通过异步备份(asynchronous replication)的,所以不是可线性化的。但是做出这个选择的原因经常是因为远距离网络的延迟,而不是仅仅为了处理数据中心的网络故障。
在 CAP 对可用性还有一致性严格的定义下,系统们表现怎么样?
拿任意一个 single master 的有备份的数据库作为一个例子。这也是标准的数据库设置。在这种情况下,如果用户不能访问 leader,就不能写到数据库。虽然他还能从 follower 那里读到数据,但是他不能写任何数据就说明它不是 CAP 可用的。更不要说这种设置还常常声称自己是“高可用的(high availablity)”。
如果以上这种设置不是 CAP 可用的,那是不是就是说他满足 CP(一致)?等一下。如果你是从 follower 那里读到的数据,因为备份是异步的,所以你可能读到旧的数据。所以你的读操作不是可线性化的,所以不满足 CAP 中的一致性。
而且支持 snapshot isolation/MVCC 的数据库是故意做成不可线性化的。否则会降低数据库的并发性。比如 PostgreSQL 的 SSI 提供的是可串行化而不是可线性化,Oracle 两者都不支持。仅仅因为数据库标榜自己是 ACID 并不意味着它就满足 CAP 中的一致性。
所以这些系统既不是 CAP 一致的,也不是 CAP 可用的。他们既不是 CP 也不是 AP,他们只是 P,不管这是什么意思。(是的,“三选二”也允许你只从三个中选一个,甚至一个都不选!)
那 NoSQL 怎么样的?拿 MongoDB 作为一个例子:每一个 shard 都只有一个 leader(至少只要他不在 split-brain 的模式下,它应该是这样的),根据以上的论证,那就说明他不是 CAP 可用的。而且 Kyle 最近发现,设置了最强的一致性,他还是允许非一致性的读操作,所以它也不是 CAP 一致的。
那像 Riak, Cassandra 还有 Voldemort 这些声称是 AP 的高可用的 Dynamo 的继承者们又怎么样呢?这取决于你的设置。如果你接受读写只访问一个拷贝(R=W=1),那么这确实是 CAP 可用的。但是如果你要求 quorum 读写(R+W>N),而且你有网络分区,那么那些被分在少部分节点的用户就不能达到 quorum,所以 quorum 操作不是 CAP 可用的(至少暂时是不可用的,直到你在少部分的分区内加入了更多的节点)。
你有时候会看到人们声称 quorum 读写可以保证可线性化,但是我觉得依赖这样的声明是不明智的。因为在一些复杂的情况下,read repair 操作和 sloppy quorum 同时发生,就有可能会重写已经被删除了的数据。或者当备份数(replicas)已经低于原来的 W 值(违反了 quorum 的条件),或者当备份数被加到了高于原来的 N 值(还是违反了 quorum 的条件),这些都可以导致不可线性化的访问结果。
这些都不是差的系统:他们在实际运用中都很成功。但是目前为止,我们还是不能严格把他们分类为 AP 或者 CP,要么是因为取决于具体的设定,或者是因为这个系统一致性和可用性都不满足。
那 ZooKeeper 又怎么样呢?他用了 consensus 算法,所以人们一般认为他是很清楚的选择了一致性而放弃了可用性(也就是CP 系统)。
但是如果你阅读 ZooKeeper 的文档,他们很清楚的说了 ZooKeeper 的默认设置不提供可线性化的读操作。每一个连接到一个服务器的客户端,当你要读的时候,即使别的节点有更新的数据,你只能看到那个服务器本地的数据。这样读操作就比需要收集 quorum 或者访问 leader 要更快。但这也说明 ZooKeeper 默认不满足 CAP 的一致性定义。
做可线性化的读操作在 ZooKeeper 中是支持的。你需要在读操作之前发一个 sync 命令。但这不是默认的设置,因为这样读操作会更慢。人们有时候会用 sync 命令,但一般不会是所有的读操作都用。
那 ZooKeeper 的可用性呢?他要求达到大多数 quorum,来达到共识,才能处理一个写操作。如果你有网络分区,一边有大多数节点,一边有少部分节点。那么拥有大多数节点的分区还可以继续工作,但是少部分节点的分区就算节点们都正常工作着,还是不能处理写操作。所以 ZooKeeper 的写操作在网络分区的情况下,不满足 CAP 的可用性(即使拥有大多数节点的分区还是可以处理写操作的)。
更有意思的是,ZooKeeper 3.4.0 还加入了一个只读的模式。在这个模式下,少部分节点的分区还可以继续处理读操作 -- 不需要 quorum! 这个读操作是满足 CAP 可用性的。所以 ZooKeeper 默认设置既不是一致的(CP)也不是可用的(AP),只是"P"。但是你有选择通过用 sync 命令来让它成为 CP。并且在正确的设置下,读操作(不包括写)其实是 CAP 可用的。
这让人不是很舒服。如果就因为 ZooKeeper 的默认设置不是可线性化的就称他为不一致,那就歪曲了他的功能。他其实可以提供非常强的一致性!他支持 atomic broadcast(这个可以约化为共识问题)以及每个 session 的 causal consistency -- 这比 read your writes, monotonic reads 还有 consistent prefix reads 在一起都要强。他的文档上说 ZooKeeper 提供可串行化的一致性,但这其实是过于谦虚了,因为他其实可以提供更强的一致性。
根据 ZooKeeper 的例子,你就会发现就算这系统在网络分区的时候既不是 CP 也不是 AP(甚至在默认设置下,就算没有网络分区,也不是可线性化的),但他还是很合理的。(我猜 ZK 在 Abadi 的 PACELC 的框架下是 PC/EL,但我不觉得这比 CAP 更有启发性。)
事实上我们都没有成功地把一个数据库无歧义地分类为 AP 或者 CP。这应该告诉我们 CP/AP 根本就不是合适的用来描述系统的标签。
我相信我们应该不要再把数据库归类为 AP 或者 CP 了,因为
如果 CP 和 AP 用来描述和评论系统是不合适的,那么我们应该用什么呢?我不认为有一个唯一的答案。很多人花了很多心思考虑这些问题,也提出了术语和模型来帮助我们理解这些问题。想要学习这些思想,你就需要更深入自己阅读文献。
不管你选择哪一种学习方式,我都鼓励你保持好奇心和耐心,因为这不是容易的学科。但是这是有回报的,因为你学会如果考虑取舍,进而搞清楚什么样的架构对于你的应用是最合适的。但是不管你做什么,请不要再说 CP 还有 AP 了,因为根本不合理。
标签:根据 tag ash split cap 范围 memory oracl 通信
原文地址:https://www.cnblogs.com/CQqf2019/p/10947945.html