当前位置: 首页 > 科技观察

请停止将数据库称为CP

时间:2023-03-19 02:12:40 科技观察

在JeffHodges的精彩博客文章《给年轻人关于分布式系统的笔记》中,他建议我们使用CAP定理对系统进行评论。许多人听取了这个建议,将他们的系统描述为“CP”(一致性但在网络分区期间不可用)、“AP”(可用但在网络分区期间不一致)或有时是“CA”(注意“我没有读过Coda的五个-岁的文章”)。我同意杰夫的所有观点。只有他关于CAP定理的观点我不得不不同意。CAP定理本身过于简单并且被广泛误解,在描述系统方面没有多大用处。所以我要求我们停止引用CAP定理,停止讨论CAP定理。相反,我们应该更准确地理解我们系统的权衡。(是的,我意识到我不想让人们再讨论这个话题很讽刺,但我正在写一篇关于它的博客文章。但至少这样,当人们问我为什么我不喜欢讨论CAP定理时以后,我会把这篇文章的链接给他。另外,抱歉吐槽,但至少吐槽有参考。)如果你想引用CAP作为定理(而不是模糊的),CAP使用非常精确的定义,用来做数据库营销概念),你需要使用非常精确的定义。数学需要精确。只有当你的措辞与定理证明中的定义相同时,证明才有意义。CAP证明使用非常具体的定义。CAP中的一致性意味着线性化。这是一种非常特殊(也非常强)的一致性。特别说明一下,ACID中的C虽然也是一致性(Consistency),但是这里和一致性无关。稍后我将解释线性化的含义。可用性(Availability)在CAP中被定义为“每一个请求(request)如果被一个工作的[数据库]节点接收到,它必须返回一个[非错误]的结果”。请注意,仅部分节点可以处理此请求是不够的。任何工作节点都必须能够处理此请求。那么多自称“高可用”的系统,通常不符合这里的可用性定义。分区容错(PartitionTolerance)基本上意味着通信是在异步网络中进行的。信息可能会延迟或丢失。互联网和我们所有的数据中心都具有此属性。所以我们在这件事上别无选择。另请注意,CAP并没有描述任何旧系统,而是一个非常具体的系统:CAP系统的模型是一个只能读写单一数据的寄存器。这就是全部。CAP没有提及涉及多个对象的事务。它们完全超出了该定理的范围,除非您可以将问题简化为单个寄存器问题。CAP定理仅考虑网络分区的故障情况(即节点仍在运行,但它们之间的网络不再工作)。这种故障肯定会发生,但它并不是唯一可能出错的地方。节点可能崩溃或完全重启,您可能没有足够的磁盘空间,您可能有软件错误等。构建分布式系统时,您需要考虑更多问题。如果过于关注CAP,很容易忽略其他重要问题。而且CAP根本没有提到延迟。通常人们实际上更关心延迟而不是可用性。事实上,满足CAP可用性的系统可以在保持可用性属性的同时花费任意长的时间来回复请求。让我冒险说,如果您的系统需要两分钟来加载一个页面,您的用户就不会称其为“可用”。如果您的措辞符合CAP认证中的精确定义,那么它就适合您。但是,如果您的一致性和可用性有其他含义,那么您就不能指望CAP仍然适用于您。当然,这并不意味着你可以通过重新定义一些词汇来做一些不可能的事情!它只是意味着你不能依靠CAP给你方向,你不能依靠CAP来证明你的观点。如果CAP定理不适用,则意味着您必须自己做出权衡。你必须根据你自己对一致性和可用性的定义来考虑这些属性,如果你能证明你自己的定理就更好了。但请不要将其称为CAP定理,因为该名称已被使用。线性化如果您对线性化(即CAP中的一致性)不是很熟悉,请让我简要解释一下。正式的定义不是特别直观,但非正式描述的关键思想是:如果操作B在操作A成功完成之后,那么整个系统必须对操作B表现为已经完成或更新了操作A的状态。更清楚,让我们看一个例子。本例中的系统不可线性化。看看下面的图片(我未出版的书的预览):这张图片显示爱丽丝和鲍勃在同一个房间里,正在检查他们的手机以了解2014年世界杯的最终结果。就在最终结果公布之后,爱丽丝刷新了页面,看到了获胜者的公布,兴奋地告诉了鲍勃。Bob立即在手机上重新加载了页面,但他的请求被发送到数据库的副本,尚未获取最新数据,他的手机显示决赛仍在进行中。如果Alice和Bob同时刷新并得到不同的结果,也就不足为奇了。因为他们不知道具体的服务器先处理了其中的哪一个。但是鲍勃知道,在爱丽丝告诉他最终结果后,他刷新了页面。所以他期望他的查询结果一定比爱丽丝的更新。事实是,他得到的是旧结果。这违反了线性化。只有当Bob通过另一个通信渠道知道Alice的结果时,Bob才能知道他的请求一定是在Alice之后。如果鲍勃没有听爱丽丝说游戏结束了,他也不知道他看到的结果是旧的。如果您正在构建数据库,您不知道用户还有哪些其他沟通渠道。所以,如果你想提供线性一致性(CAP一致性),你需要让它看起来好像你的数据库只有一个副本,尽管实际上可能在多个地方有多个备份。这是一个非常昂贵的属性,因为它需要你做很多协调工作。甚至您计算机上的CPU也不提供对本地内存的线性化访问!在现代CPU上,您需要使用内存屏障指令来实现线性化访问。甚至测试一个系统是否可线性化也很困难。CAP可用性让我们简要讨论一下为什么我们在网络分区的情况下放弃可用性和一致性之一。例如,您的数据库在两个不同的数据中心有两个副本。备份是如何完成的并不重要,它可以是单主机,或多个领导者,或基于仲裁的备份(Dynamo使用的方式)。要求是当数据写入一个数据中心时,也必须写入另一个数据中心。假设客户端只连接其中一个数据中心,连接两个数据中心的网络出现故障。所以现在假设网络出现故障,这就是我们所说的网络分区。那么接下来呢?显然你有两个选择:你的应用程序仍然被允许写入数据库,所以双方的数据库仍然完全可用。但是一旦两个数据库之间的网络宕机,一个数据中心的任何写操作都不会出现在另一个数据中心。这违反了线性一致性(使用前面的示例,Alice可能连接到数据中心#1,而Bob连接到数据中心#2)。如果你不想失去线性化,你必须确保你的读写操作在同一个数据中心,你可以称之为领导者。另一个数据中心因为网络故障无法更新,所以必须停止接收读写操作,直到网络恢复,两边数据库重新同步。所以非leader数据库虽然运行正常,但是无法处理请求,违反了CAP的可用性定义。(而这,其实就是CAP定理的一个证明,仅此而已。这里的例子使用了两个数据中心,但是同样适用于一个数据中心的网络故障。我只是觉得使用两个数据中心更好。它是很容易想到这个问题。)注意上面的第二点,即使它违反了CAP的可用性,我们仍然成功地处理了请求。所以当一个系统可线性化(即CAP不可用)时,并不一定意味着网络分区一定会导致应用停止。如果你能把用户流量导流到领导者数据库,那么用户根本不会注意到任何问题。实际应用中的可用性并不等同于CAP可用性。你的应用程序的可用性主要是通过SLA来衡量的(比如99.9%的正确请求必须在一秒内成功返回),但一个系统实际上可以满足这样的SLA,不管它是否满足CAP可用性。在实践中,跨越多个数据中心的系统通常是通过异步复制来备份的,因此它们是不可线性化的。但之所以做出这样的选择,往往是因为远距离的网络延迟,而不仅仅是为了应对数据中心的网络故障。许多系统既不可线性化也不可CAP。在CAP对可用性和一致性的严格定义下,系统的性能如何?以任意一个带备份的单主库为例。这也是一个标准的数据库设置。在这种情况下,如果用户无法访问领导者,他们就无法写入数据库。虽然他仍然可以从follower读取数据,但是如果他不能写入任何数据,则意味着它对CAP不可用。更不用说这种设置经常声称是“高可用性”。如果以上设置CAP都不具备,是否说明他满足CP(一致)?等一下。如果从follower读取数据,因为备份是异步的,可能会读取到旧数据。所以你的读操作是不可线性化的,所以不满足CAP中的一致性。此外,支持快照隔离/MVCC的数据库有意不可线性化。否则会降低数据库的并发度。比如PostgreSQL的SSI提供的是序列化而不是线性化,Oracle也不支持。仅仅因为数据库将自己宣传为ACID并不意味着它满足CAP中的一致性。所以这些系统既不符合CAP也不可用。他们既不是CP也不是AP,他们只是P,不管那是什么意思。(是的,“三选二”也允许您在三者中只选择一个,甚至一个都不选!)那么NoSQL呢?以MongoDB为例:每个shard只有一个leader(至少只要他不在裂脑模式下,就应该是这样),按照上面的说法,就是说他不可用于帽。而Kyle最近发现,即使是最强的一致性集,他仍然允许非一致性读操作,所以不是CAP一致性。Dynamo的后继者如Riak、Cassandra和Voldemort自称是高可用性AP的情况如何?这取决于您的设置。如果您只接受对一个副本的读写访问权限(R=W=1),那么这确实是可用的CAP。但是如果你要求quorum读写(R+W>N),而且你有网络分区,那么那些被分到少量节点的用户就达不到quorum,所以quorum操作对CAP来说是不可用的(at至少暂时不可用,直到您将更多节点添加到较少数量的分区)。有时您会看到有人声称法定人数读取和写入保证可线性化,但我认为依赖此类声明并不明智。因为在一些复杂的情况下,读修复操作和sloppyquorum同时发生,有可能重写已经被删除的数据。要么当副本数已经低于原来的W值(违反法定人数条件),要么当副本数增加到高于原来的N值(再次违反法定人数条件)时,这些都会导致非线性优化访问结果。这些都不是坏系统:它们在实践中取得了成功。但到目前为止,我们还不能严格地将它们归类为AP或CP,要么是因为要看具体的设定,要么是因为这个系统的一致性和易用性不尽如人意。案例分析:ZooKeeper那么ZooKeeper呢?他用的是共识算法,所以人们普遍认为他明显选择了一致性,放弃了可用性(即CP系统)。但是如果你阅读ZooKeeper文档,他们清楚地表明ZooKeeper的默认设置不提供可线性化的读取操作。对于每个连接到一个服务器的客户端,当你想读取的时候,即使其他节点有更新数据,你也只能看到该服务器本地的数据。通过这种方式,读取操作比需要收集仲裁或访问领导者更快。但是这也说明ZooKeeper默认不满足CAP的一致性定义。ZooKeeper支持执行可线性化的读取操作。您需要在读取操作之前发出同步命令。但这不是默认设置,因为读取操作会变慢。人们有时会使用sync命令,但一般不会用于所有读取操作。ZooKeeper的可用性如何?他需要多数法定人数达成共识才能处理写操作。如果你有一个网络分区,一侧有大多数节点,另一侧有少数节点。那么节点数最多的分区可以继续工作,但是节点数少的分区即使正常工作也无法处理写操作。因此,ZooKeeper的写操作不满足CAP在网络分区情况下的可用性(即使拥有最多节点的分区仍然可以处理写操作)。更有意思的是ZooKeeper3.4.0还增加了只读模式。在这种模式下,少数节点的分区可以继续处理读操作——不需要quorum!该读取操作满足CAP可用性。所以ZooKeeper默认设置既不是一致的(CP)也不是可用的(AP),只是“P”。但是您可以选择使用sync命令使其成为CP。如果设置正确,读取操作(不包括写入)实际上可用于CAP。这不是很舒服。仅仅因为ZooKeeper的默认设置不可线性化就调用它不一致是在歪曲其功能。他实际上可以提供非常强的一致性!他支持原子广播(这可以归结为一个共识问题)和每个会话的因果一致性——这比读你的写、单调读和一致性前缀读一起强。他的文档说ZooKeeper提供了serializableconsistency,但是这个其实太谦虚了,因为他其实可以提供更强的一致性。根据ZooKeeper的例子,你会发现即使在网络分区时系统既不是CP也不是AP(即使在默认设置下,即使没有网络分区也是不可线性化的),但仍然很合理的。(我猜测ZK是Abadi的PACELC框架中的PC/EL,但我认为这并不比CAP更有启发性。)CP/AP:伪二分法数据库明确分类为AP或CP。这应该告诉我们CP/AP根本不是描述系统的合适标签。我认为我们应该停止将数据库分类为AP或CP,因为:您可能在同一个软件中有多种一致性属性选择许多系统在CAP的定义下既不一致也不可用。但是我从来没有听说过有人称这些系统为“P”,可能是因为它不是很漂亮。但这还不错,他可能是一个非常合理的设计,他只是不属于CP/AP类别。虽然大部分软件不属于CP/AP这两类,但是人们还是强行将软件归入这两类。这导致为了适用而不可避免地改变“一致性”或“可用性”的定义。不幸的是,如果术语的定义发生变化并且CAP定理本身不适用,那么CP/AP的区别就完全没有意义了。将系统分为这两类导致许多细节被忽略。在考虑分布式系统设计时,有很多关于容错、延迟、简单模型、运行成本等方面的考虑。将如此多的细节编码为一点信息显然是不可能的。例如,尽管ZooKeeper具有AP只读模式,但该模式还为所有写操作提供了总排序。这比Riak或Cassandra等AP系统提供的保护要强得多。所以简单地将它们都归为一类AP似乎是不合理的。甚至EricBrewer也承认CAP是一个具有误导性和过于简单化的模型。2000年,CAP的意义在于让大家开始讨论分布式系统的权衡。他在这方面做得很好。但它不作为正式的突破性结果,也不是严格的数据系统分类方法。15年后,我们有了更多具有不同一致性和容错模型的系统。CAP已经完成了自己的使命,现在是时候放下它了。学会独立思考如果CP和AP不适合描述和批判系统,那么我们应该用什么?我认为没有单一的答案。许多人对这些问题进行了很多思考,并提出了术语和模型来帮助我们理解它们。要了解这些想法,您需要自己深入研究文献。DougTerry的论文是一个不错的起点。其中,他用棒球来解释各种最终一致性。即使对于像我这样根本不懂棒球的非美国人来说,也非常易读且解释清楚。如果你对事务的隔离模型感兴趣(这和分布式系统的一致性不是一回事,而是相关的),可以看看我的小项目Hermitage。本文讨论了分布式系统的一致性与事务的隔离性和可用性之间的关系。(这篇论文还描述了不同索引之间的分级。KyleKingsbury喜欢告诉人们这一点。)读完这篇文章后,您应该准备好深入阅读这篇论文。我在这篇文章中包含了许多对文献的参考。看看吧,很多高手已经为你解决了很多问题。作为最后的手段,如果你不想阅读原始论文,我建议你阅读我的书。本书以通俗易懂的方式总结了大部分重要思想。(你看,我设法让这篇文章看起来不像是想推销我的书。)如果你想了解更多关于如何正确使用ZooKeeper的知识,FlavioJunqueira和BenjaminReed的书非常好。无论您选择哪种学习方式,我都鼓励您保持好奇心和耐心,因为这不是一门容易的学科。但它是有回报的,因为你学会了如何权衡取舍,然后找出最适合你的应用程序的架构。但是不管你做什么,请不要再谈CP和AP了,因为那根本就没有意义。