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

这可能是我看过最通俗也是最深刻的CAP理论

时间:2023-03-20 17:25:08 科技观察

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