文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

请不要再称数据库是CP或者AP

2024-12-03 04:55

关注

我同意Jeff的所有观点。唯独他关于CAP定理的观点,我必须表示不同意。CAP定理本身太简单化而且被广泛的误解,以至于在描述系统上没有太多用处。因此我请求我们不要再引用CAP定理,不要再讨论CAP定理。取而代之,我们应该用更精确的术语来理解我们系统的权衡。

[[399570]]

(没错,我意识到很讽刺的是我不希望别人再讨论这个话题,但我却正在一篇关于这个话题的博客文章。但是至少这样以后别人问我为什么不喜欢讨论CAP定理的时候,我可以把这篇文章的链接给他。还有,抱歉这篇文章有些吐槽,但是至少这个吐槽有文献引用。)

CAP用的是非常精确的定义

如果你想引用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 指令来达到可线性化访问。甚至测试一个系统是不是可线性化的也是很困难的。

CAP可用性

让我们来简短的讨论一下为什么在网络分区的情况下,我们要放弃可用性和一致性中的一个。

举个例子,你的数据库有两个拷贝在两个不同的数据中心。具体怎么做备份并不重要,可以是single-master,或者多个leader,或者基于quorum的备份(Dynamo使用的方式)。要求是当数据被写到一个数据中心的时候,他也一定要被写到另一个数据中心。假设client只连接到其中一个数据中心,而且连接两个数据中心的网络故障了。

那么现在假设网络中断了,这就是我们所说的网络分区的意思。接下来怎么样呢?

显然你有两个选择:

  1. 你的应用还是被允许写到数据库,所以两边的数据库还是完全可用的。但是一旦两个数据库之间的网络中断了,任何一个数据中心的写操作就不会在另一个数据中心出现。这就违反了可线性化(用之前的例子,Alice可能链接到了一号数据中心,而Bob连接到了二号数据中心)。
  2. 如果你不想失去可线性化,你就必须保证你的读写操作都在同一个数据中心,你可能叫这它leader。另一个数据中心,因为网络故障不能被更新,就必须停止接收读写操作,直到网络恢复,两边数据库又同步了之后。所以虽然非leader的数据库在正常运行着,但是他却不能处理请求,这就违反了CAP的可用性定义。

(而这个,其实就是CAP定理的证明。这就是全部了。这里的例子用到了两个数据中心,但是对于一个数据中心内的网络故障也是同样适用的。我只是觉得用两个数据中心这样更容易考虑这个问题。)

注意到上面第二点,就算它违反了CAP的可用性,但我们还是在成功地处理着请求。所以当一个系统选择了可线性化(也就是说不是CAP可用的),这并不一定意味着网络分区一定会造成应用停运。如果你可以把用户的流量转移到leader数据库,那么用户根本就不会注意到任何问题。

实际应用中的可用性和CAP可用性并不相同。你应用的可用性多数是通过SLA来衡量的(比如99.9%的正确的请求一定要在一秒钟之内返回成功),但是一个系统无论是否满足CAP可用性其实都可以满足这样的SLA。

实际操作中,跨多个数据中心的系统经常是通过异步备份(asynchronous replication)的,所以不是可线性化的。但是做出这个选择的原因经常是因为远距离网络的延迟,而不是仅仅为了处理数据中心的网络故障。

很多系统既不是可线性化的也不是CAP可用的

在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

那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更有启发性。)

CP/AP:一个伪二分法

事实上我们都没有成功地把一个数据库无歧义地分类为AP或者CP。这应该告诉我们CP/AP根本就不是合适的用来描述系统的标签。

我相信我们应该不要再把数据库归类为AP或者CP了,因为:

学会独立思考

如果CP和AP用来描述和评论系统是不合适的,那么我们应该用什么呢?我不认为有一个唯一的答案。很多人花了很多心思考虑这些问题,也提出了术语和模型来帮助我们理解这些问题。想要学习这些思想,你就需要更深入自己阅读文献。

不管你选择哪一种学习方式,我都鼓励你保持好奇心和耐心,因为这不是容易的学科。但是这是有回报的,因为你学会如果考虑取舍,进而搞清楚什么样的架构对于你的应用是最合适的。但是不管你做什么,请不要再说CP还有AP了,因为根本不合理。

 

来源:Dockone.io内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯