CAP Theorem in Distributed Systems

171 阅读11分钟

Overview

分布式系统是指:多个节点通过网络相互连接组成的系统,而 CAP 订立是分布式系统中的基本定理,它包含三个方面:

  • C 代表的是一致性(Consistency)
  • A 代表的是可用性(Avaiablility)
  • P 代表的是分区容忍性(Partiton Tolerance)

该定理规定:在分布式系统中,一致性、可用性和分区容忍性只能三者选其二,如果需要增强其中一个方面,就意味着必须牺牲其他方面的特性;

在理解这句话之前,先理解上述术语。

Consistency

一致性:一个分布式系统中的所有节点的数据是一致的,拥有相同的数据视图(即每个节点看到的数据都是一样的),所以每次读取都会返回最近写入的最新数据。一旦成功写入任何值,随后的读取将返回该值。

Availability

可用性:分布式系统应该能够在合理的时间内向每个请求返回无错误的响应,但并不保证响应最新数据。

Partition Tolerance

分区容忍性:即使发生了网络分区,分布式系统仍然继续正常运行。这意味着即使节点之间存在一些网络问题(消息故障、数据包丢失),系统也可以继续运行,而不是完全无法对外提供服务了。换句话说,系统能够容忍网络分区。

在实际分布式系统中,分区容忍性是必须保证的特性。

CAP Theorem

CAP 定理指出,在分布式系统中,您只能实现上述任意三个属性中的两个,组合形式如下:

  • CA
  • CP
  • AP

换句话说,分布式系统不可能同时具备一致性、可用性和分区容忍性。

下面给出 CAP 理论的经典配图

image.png

Consistency and Availability

如果您的系统不能容忍网络分区,那么可以同时实现一致性和可用性

Consistency and Partition Tolerant

如果您的系统能够容忍网络分区,那么您可以以牺牲可用性为代价来实现一致性

比如强一致性算法 Raft,在出现脑裂(网络分区)的情况下,可能导致系统暂时不可用;这是一个牺牲可用性保证分区容忍性的例子。

Availability and Partition Tolerant

如果您的系统能够容忍网络分区,那么您可以以一致性为代价来实现可用性

比如放弃了强一致性的 BASE 理论,为了实际生产环境中的高可用性,牺牲了一致性保证,只提供最终一致性的保证。

如我们之前提到的,系统必须具有分区容忍性,因为网络必然会发生故障,节点之间的通信必然会丢失。尽管网络故障或消息丢失,它应该继续工作。由于 P 对于系统来说应该始终为真,因此我们只能实现 C 或 A。换句话说,只有当您的系统具有分区容忍性时,我们才能实现一致性或可用性。

举个例子来理解上述这段话,而要正确理解这个例子,我们首先需要正确理解 CAP 中的术语“一致性”。当我们在这里提到一致性时,我们指的是强一致性。

如果系统读取的是最新写入的内容,那么它就是强一致性系统。在强一致性系统中永远不会返回过时或旧的值。考虑一个有两个节点的分布式系统,我们分别看下一致和不一致的例子:

Example of a consistent case

起初,节点 n0 和 n1 中的数据项 "A" 的值都是 1,从任一节点读取 "A" 的值都是 1。 现在,假设两个节点上的 "A" 值都更新为2,那么再次从任一节点读取数据,"A" 的值都是 2,这就是状态一致的例子。

Example of an inconsistent case

起初,节点 n0 和 n1 中的数据项 "A" 的值都是 1,从其中任何一个节点读取都会返回 "A" 的值 1。现在,我们将第一个节点上 "A" 的值更新为 2,但是这次更新还没有同步给第二个节点;因此,从第一个节点读取时,"A" 的值将返回为 2,而从第二个节点读取时,"A" 的值将返回为 "1",这就是状态不一致的一个例子。

现在我们可以看看强一致性的公式了。

The formula for Strong Consistency

要理解数据库中的强一致性,我们首先需要理解三个术语

  • Number of Nodes or Replicas(节点或副本数量)
  • Write Quoram(新数据需要写到几个节点上)
  • Read Quoram(读取数据时需从多少个节点读)

Number of Nodes or Replicas

这是系统中存在的节点数。当我们在这里说节点时,它本质上意味着具有相同数据的副本的数量

Write Quoram

这是在写入成功之前所要写入的最小节点数量,换句话说,如果一个写入操作成功,那么这个数据至少已经写到了 Write Quoram 个节点上。(比如 Write Quoram = 2,我们要写入数据项 A 的值为 1,这个写入操作成功的前提是,它已经将 A=1 写到了集群中的 2 个节点上,这是我们才认为写入操作成功)

Read Quoram

这是在读取成功返回之前需要的最小节点数。它指的是:从 Read Quoram 个节点返回的数据相同,那么读取才是有效的,否则读取将被拒绝。(比如 Read Quoram = 2,那么一个读操作成功需要:从集群中 2 个节点读取数据,并且它们返回的结果是一样的,这时我们才认为读取操作成功)

从数学上讲,如果 Write Quoram 和 Read Quoram 的总和大于读取节点的数量,则系统被称为强一致。

即如果 Write Quoram为 W,Read Quoram 为 R,节点数为 N,则

  • 如果 W+R > N,则系统具有强一致性。
  • 如果 W+R <= N,则不能保证系统具有强一致性

换句话说,如果 Write Quoram 节点和 Read Quoram 节点之间存在共同的节点,那么系统将是强一致的,因为公共节点(两个节点集合中的交集)将拒绝对过时数据读取。

每当 W + R > N 时,Write Quoram 和 Read Quoram 之间的节点一定是存在交集的。从数学上讲,要知道原因并不困难。

让我们通过一些例子来看看这个公式是如何成立的,我们将看到以下几种情况

  • The number of nodes is 1

  • The number of nodes is 2

  • The number of nodes is 3

The number of nodes is 1 集群中节点(副本)数量为 1

在这种情况下,Write Quoram 和 Read Quoram 个数的唯一可能性是 1 和 1。因此 Write Quoram 和 Read Quoram 的和大于节点数,又因为只有一个实例,所以读和写都发生在同一个实例上,因此读总是最新的。

我们证明了第一种情况下,公式 W+R > N 成立的情况下,系统是强一致性成立

The number of nodes is 2

在这种情况下,又存在额外的四种情况

  • Write Quoram is 2 and Read Quoram is 1 – Strongly Consistent

  • Write Quoram is 1 and Read Quoram is 2 – Strongly Consistent

  • Write Quoram is 2 and Read Quoram is 2 – Strongly Consistent

  • Write Quoram is 1 and Read Quoram is 1 – Not Strongly Consistent

Write Quoram is 2 and Read Quoram is 1

在这种情况下,Write Quoram 和 Read Quoram 之和为 3,大于节点数 2,因此系统应该是强一致的。在这种情况下,由于写入发生在两个节点上,因此从任一节点读取将返回相同的数据,并且系统整体将是强一致的。

Write Quoram is 1 and Read Quoram is 2

同样,在这种情况下,Write Quoram 和 Read Quoram 的总和为 3,大于集群节点总数,因此系统应该是强一致的。在这种情况下,写入仅发生在单个实例上。但两个节点都发生读取。想象一下这样的情况,起初两个节点上名为 A 的数据项的值为 1。现在写入发生在第一个节点上,它的 A 数据项的值更改为 2。由于 Write Quoram 为 1,因此写入第一个节点后就会成功返回。而在节点 2 与节点 1 上的最新数据同步之前,发生了读取。由于 Read Quoram 为 2,因此将从两个节点读取。第一个节点将返回 A 的值为 2,第二个节点将返回 A 的值为 1。由于两个节点返回的值不相同,系统将拒绝这次读取以保持强一致性。

所以我们说在强一致性系统中:要么返回读取失败,要么返回最新的数据,绝不会返回过时的数据(这是牺牲可用性换来的)

Write Quoram is 2 and Read Quoram is 2

在这种情况下,Write Quoram 和 Read Quoram 的总和为 4,大于节点数,因此系统应该是强一致的。因为写入发生在两个节点上,并且读取也发生在两个节点上。因此每次读取的都是最新的。

Write Quoram is 1 and Read Quoram is 1

在这种情况下,Write Quoram 和 Read Quoram 的总和为 2,等于节点数 2,因此系统不是强一致的。假设刚开始时数据项 A 在两个节点上的值都是 1,有一个新的写入操作发生在节点 1 上,将节点 1 上的 A 修改为 2;而在最新数据同步到节点 2 之前,一次新的读取发生在节点 2 上,那么返回 A 的值为 1,又因为 Read Quoram = 1,这意味着读取成功,但这时候读取的是陈旧数据,所以系统不是强一致性的。

Proof Of CAP Theorem

现在你已经对强一致性有了认识,现在让我们试着更好地理解 CAP 定理。

假设系统中有两个节点,这两个节点相互连接,并保持同步。

根据上述公式,我们已经知道,如果有两个节点,当出现以下情况时,系统将具有强一致性

  • Write Quoram is 2 and Read Quoram is 1
  • Write Quoram is 1 and Read Quoram is 2
  • Write Quoram is 2 and Read Quoram is 2

系统在以下情况下不是强一致性的

  • Write Quoram is 1 and Read Quoram is 1

我们在前面已经提到,在一个容忍分区的分布式系统中,只能实现一致性或可用性。

让我们看看如何做到这一点:假设两个节点之间发生了网络分区,并且第二个节点不可用

How consistency is achieved at the cost of Availability

这个例子中节点数为 2,根据上面的说明,我们知道想要实现强一致性系统我们有三种选择:

  • Write Quoram is 2 and Read Quoram is 1
  • Write Quoram is 1 and Read Quoram is 2
  • Write Quoram is 2 and Read Quoram is 2

在第一种情况下,由于 Write Quoram 为 2,因此写入操作必须同时写入两个节点后才算成功。但由于第二个节点不可用,因此它将拒绝写入(系统无法写入)

在第二种情况下,由于 Read Quoram 为 2,因此读取操作必须从两个节点读取并返回相同结果才算成功。但由于第二个节点不可用,因此它将拒绝读取(系统无法读取)

在第三种情况下,由于 Write Quoram 为 2,因此它必须写入两个节点,并且由于 Read Quoram 为 2,因此它必须从两个节点读取。但由于第二个节点不可用,因此它将拒绝读取和写入(系统无法读取和写入)

从这三种情况可以看出,在网络分区的情况下,我们以牺牲可用性为代价获得了强一致性。因此我们这里只实现了 C(Consistency) 和 P(Partition Tolerance),而且无论如何我们都无法获得 A(Availability)。

在容忍网络分区的情况下,要想实现强一致性,必须牺牲可用性

How availability is achieved at the cost of Consistency

为了实现可用性,我们必须放弃一致性。

继续以上面的例子讲解(2 个节点的分布式系统且节点 2 不可用),只有一种选择使系统不具有强一致性:

  • Write Quoram is 1 and Read Quoram is 1

Write Quoram 为 1,虽然节点 2 不可用,但我们可以写入节点 1;

Read Quoram 为 1,虽然节点 2 不可用,但我们可以从节点 1 读取,所以系统写入和读取都可用。

Write Quoram 和 Read Quoram 之和为 2,等于节点数。

由公式可知,当 Write Quoram 和 Read Quoram 之和小于等于节点数时,则系统不是强一致的。因此,在这种情况下,我们有可用性,但系统不具有一致性。

因此,我们在这里只能获得 A 和 P,无法同时再获得 C,这就是 CAP 定理的证明。

Conclusion

以上就是关于分布式系统中的 CAP 定理的全部内容,希望你喜欢这篇文章。

请在评论中分享反馈意见。