今天和大家介绍下CAP定理。


什么是CAP定理

在了解什么是CAP定理之前,我们分别看下C,A,P是什么?

  • 一致性(Consistency):你对一个系统执行写操作后,紧接着的一次读操作能够获取正确的值。
  • 可用性(Avaliablity):在任意时刻对系统的请求都能得到及时响应
  • 分区容错性(Partition Tolerance):你的分布式系统允许任意节点之间的任意数量消息丢失。

为了方便大家理解,我下面使用图例来解释着三点的含义。

我们先建立一个具有2个节点的分布式系统,节点之间可以相互通信,进行状态同步。

客户端可以访问任意一个节点进行数据的读写。

image-20200315145251184

一致性

我们先看一致性。

我们对节点1写入状态$S_{1}$,然后立刻从节点2进行读取,如果读取的状态也是$S_{1}$,那么我们认为这个系统是满足一致性要求的。

image-20200315145932650

有同学说这个很难么?

对于本地数据库不难,但是对于分布式系统难。

因为对于分布式系统来说,节点1和节点2可能位于不同的物理设备,甚至是不同地区,因此,必须通过其他通信方式进行数据的同步,才能在节点2得到一致的数据。

image-20200315152105951

可用性

我们再看可用性。

可用性是在任意时刻对系统的请求都能得到及时响应,对于分布式系统来说是对任意节点的请求都可以得到响应。

有同学会说,这不就是保证硬件,网络等设施的正常么?这个不应该是运维同学的事情么?

要注意CAP定理中的可用性可不是在考虑这些硬件设施的可用性。

我们可以暂时假设客户端到系统的任意节点的网络是可靠的,这样看起来可用性是完全“没问题”的。

不过这种“没问题”真的是你想要的么?注意一下任意时刻,任意节点这个关键信息。

假如我们在节点2同步完成之前,就进行了节点2的状态读取,此时读出来的数据是$S_{0}$!

我们的一致性原则被破坏了。

现在还会觉得可用性简单么?

image-20200315152212546

分区容错性

我们先跳过可用性的拷问,来看下分区容错性。

什么是分区?分区指的是分布式系统中,各个节点存在2种以上不同的状态。

我们在下图中节点1和节点2的同步期间,两个节点的状态不一致,这个时刻就是一个分区时刻。

image-20200315153444143

分区容错指的是你的系统允许出现分区的情况,并且仍然能够正常运行。

CAP定理

回到主题,什么是CAP定理?

CAP定理就是说对于分布式系统,不能够同时满足一致性,可用性和分区容错性。

我们刚刚已经看到,在允许系统存在分区的情况下(即满足分区容错性),如果要保证任意时刻任意节点可响应(即满足可用性),那么读取的数据可能就会不一致(即一致性被破坏);

而如果我们期望在分区情况下保持读取数据一致(即同时满足分区容错性和一致性),那么要么在同步状态完成前,让客户端的请求等待或者直接返回失败,要么只允许从节点1进行读取,(即可用性被破坏)。

那有办法同时保证一致性和可用性么?有,例如我们常见的在单个节点部署的关系数据库,就可以认为同时保证了一致性和可用性。但是如果你是单节点,必然不会出现分区的情况,也就无从谈满足分区容错性了。

CAP定理是指导,不是实际规则

由于CAP定理中指出不能够同时满足一致性,可用性和分区容错性,许多同学往往会陷入所谓的“三选二”的陷阱中。而由于分布式系统中,节点之间的通信故障时不可避免的,即系统必须要求满足分区容错性,部分同学甚至得出了可用性和一致性不可兼得的结论。

不要被CAP定理限制住,套用加勒比海盗中的一句台词“The CAP Theorem is more what you call guidlines, than catual rules.”CAP定理是指导,不是实际规则。

我们不需要绝对

首先我们需要明确2点:

  • CAP定理是正确的。但是CAP定理中的场景都是绝对的场景,即强一致性,任意时刻可用性,绝对的分区
  • 我们不需要绝对。

我们使用具体的场景思考下CAP定理。

假如你的业务是新闻网站,有很多的读者(比如3000个)同时(1s之内)对你的文章进行点赞,不同的读者在刷新前看到的点赞数值相差5个,对你的业务影响有多大?

假如你需要在你的文章发布的第一时刻(比如200ms之内),你的所有读者就获得最新的文章么?如果你的读者最晚在发布后的5分钟之内刷到的还是昨天的“旧闻”,分布后的第6分钟才看的这篇文件的影响有多大?

我们必须要明确的一点是,对于分布式系统来说,分区仅仅是非常短暂的一个瞬时状态,最终会通过状态同步等方式结束分区,达到一致性。

我们的业务也许不能够允许发生长达1h的分区状态,但是例如100ms的分区状态我们是可以接受的。

因此我们可以在在CAP的理论框架下,根据业务进行适当调整,实现分布式系统的高可用和最终一致。

这也是BASE理论兴起的原因(Basically Available(基本可用),Soft State(软状态)和Eventually Consistent(最终一致性))

可以参考的举措

  • 对于一致性,写完立刻读的情形有多少?不在进行写操作的客户端能不能在短时间放弃一致性,获取高可用性?
  • 对于进行写操作的客户端(如发送博客),能不能利用本地缓存数据避免立刻读取刚刚写入的数据,或者直接使用其他界面仅通知写入完成?
  • 能不能让写完的用户直接读取刚刚写入的同一节点,而不是读取还未同步的其他节点,牺牲可用性换取一致性?

后记

我们了解了CAP定理,了解了一致性,可用性和分区容错性不可以同时满足。

我们也看到我们仍然可以通过业务调整实现最终一致的高可用分布式系统。

希望我们在实施架构设计时能够更好的运用CAP定理,而不是被其限制。


参考文档:


本文会经常更新,请阅读原文: https://dotnet-campus.github.io//post/%E4%B8%80%E6%96%87%E7%9C%8B%E6%87%82CAP%E5%AE%9A%E7%90%86.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。

知识共享许可协议 本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名 dotnet 职业技术学院 (包含链接: https://dotnet-campus.github.io/ ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系