zoukankan      html  css  js  c++  java
  • CAP 超详细名词解释

    引言

    2000年7月,加州大学伯克利分校的Eric Brewer教授在ACM PODC会议上提出CAP猜想。2年后,麻省理工学院的Seth Gilbert和Nancy Lynch从理论上证明了CAP。之后,CAP理论正式成为分布式计算领域的公认定理。

    概述

    CAP 理论对分布式系统的特性做了高度抽象,形成了三个指标:

    • 一致性(Consistency)
    • 可用性(Availability)
    • 分区容错性(Partition Tolerance)

    CAP定理我们常见的描述是一个分布式系统最多只能同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance)这三项中的两项

    但布鲁尔在提出 CAP 猜想的时候,并没有详细定义 Consistency、Availability、Partition Tolerance 三个单词的明确定义。
    因此如果初学者去查询 CAP 定义的时候会感到比较困惑,因为不同的资料对 CAP 的详细定义有一些细微的差别。

    这里不展开讲述这些细微差别,本文选取Robert Greiner的文章来作为参考。

    CAP Theorem: Explained

    CAP Theorem: Revisited

    需要注意CAP Theorem: Explained这篇文章已经被标明outdated(已过时)。网上说的大多数定义是根据第一篇文章来的。

    下面带你逐步理解CAP定理中分布式一致性可用性分区容错性这4个关键词。

    分布式

    我们都知道CAP定理说的是分布式系统下的定理,但是分布式系统有很多类型,有异构的,比如节点之间是上下游依赖的关系,有同构的,比如分区/分片型的、副本型的(主从、多主)。

    那么CAP说的分布式包含以上所有吗,网上很少有提到CAP说的分布式系统是否有具体的类型。

    CAP Theorem: Explained文中描述CAP定理用的是Distributed systems,也没有具体指明CAP理论下分布式系统的特征,但是在CAP Theorem: Revisited是这样描述CAP的:

    in a distributed system (a collection of interconnected nodes that share data), you can only have two out of the following three guarantees across a write/read pair: Consistency, Availability, and Partition Tolerance - one of them must be sacrificed.

    翻译过来就是:在一个分布式系统(指互相连接并共享数据的节点的集合)中,当涉及读写操作时,只能保证一致性(Consistence)、可用性(Availability)、分区容错性(Partition Tolerance)三者中的两个,另外一个必须被牺牲。

    我们可以看到有两个很明显的差别:

    • a collection of interconnected nodes that share data: 第二篇文章中提到了CAP关心的是互联且共享数据的分布式系统,所以最简单的例如 Memcache 的集群,节点之间就没有连接和共享数据,因此 Memcache 集群这类分布式系统就不符合 CAP 理论探讨的对象;而 MySQL 集群就是互联和进行数据复制的,因此是 CAP 理论探讨的对象。
    • write/read pair: CAP 关注的是对数据的读写操作,而不是分布式系统的所有功能。例如,ZooKeeper 的选举机制就不是 CAP 探讨的对象。

    所以CAP定理的分布式系统说的是副本型的分布式系统。

    一致性

    一致性这个词在不同的环境下有着不同的含义,被极大的滥用了,导致很难理解:

    1. 多副本的一致性
    2. 一致性hash
    3. CAP理论的一致性
    4. ACID里的一致性

    而这几个一致性的含义不同。我们接触一致性最多的还是ACID中的一致性。但这和CAP中的一致性完全不是一个东西,我先告诉你ACID的一致性指的是约束一致性,而CAP中的一致性指的是数据正确。我们先来讨论CAP中的一致,最后再向你解释ACID中的一致性。

    在 Robert Greiner 的 CAP Theorem: Explained 文中是这样描述一致性的:

    [C] Consistency - All nodes see the same data at the same time.

    翻译下就是所有节点同时看到相同的数据。

    一想不对啊,多节点之前网络传输肯定有延迟啊,这里又要提到个 CAP 的知识点,就是 CAP 是忽略网络延迟的。布鲁尔在定义一致性时,并没有将延迟考虑进去。也就是说,当事务提交时,数据能够瞬间复制到所有节点。

    为了帮你理解一致性,我给你举一个具体的例子。比如,2 个节点的 KV 存储,原始的 KV 记录为“X = 1”。

    紧接着,客户端向节点 1 发送写请求“SET X = 2”。

    如果节点 1 收到写请求后,只将节点 1 的 X 值更新为 2,然后返回成功给客户端。

    那么,此时如果客户端访问节点 2 执行读操作,就无法读到最新写入的 X 值,这就不满足一致性了。

    如果节点 1 收到写请求后,通过节点间的通讯,同时将节点 1 和节点 2 的 X 值都更新为 2,然后返回成功给客户端。

    那么在完成写请求后,不管客户端访问哪个节点,读取到的都是同一份最新写入的数据,这就叫一致性。

    一致性这个指标,描述的是分布式系统非常重要的一个特性,强调的是数据正确。也就是说,对客户端而言,每次读都能读取到最新写入的数据。注意,这里说的所有节点同时看到相同的数据,后者要求所有节点数据一致,前者要求客户端看到的一致。而这也是Robert Greiner 在 CAP Theorem: Revisited中提到的一致性:

    Consistency - A read is guaranteed to return the most recent write for a given client.

    翻译下就是对某个指定的客户端来说,读操作保证能够返回最新的写操作结果。

    第一版解释和第二版解释的主要差异点表现在:

    • 第一版从节点 node 的角度描述,第二版从客户端 client 的角度描述。

    相比来说,第二版更加符合我们观察和评估系统的方式,即站在客户端的角度来观察系统的行为和特征。

    • 第一版的关键词是 see,第二版的关键词是 read。

    第一版解释中的 see,其实并不确切,因为节点 node 是拥有数据,而不是看到数据,即使要描述也是用 have;

    • 第二版从客户端 client 的读写角度来描述一致性,定义更加精确。

    第一版强调同一时刻拥有相同数据(same time + same data),第二版并没有强调这点。这就意味着实际上对于节点来说,可能同一时刻拥有不同数据(same time + different data),这和我们通常理解的一致性是有差异的,为何做这样的改动呢?其实在第一版的详细解释中已经提到了,具体内容如下:

    A system has consistency if a transaction starts with the system in a consistent state, and ends with the system in a consistent state. In this model, a system can (and does) shift into an inconsistent state during a transaction, but the entire transaction gets rolled back if there is an error during any stage in the process.

    翻译下:

    如果事务以系统处于一致状态开始,并以系统处于一致状态结束,则系统具有一致性。 在此模型中,系统可以(并且确实)在事务期间转换为不一致状态,但是如果在过程的任何阶段出现错误,则整个事务都会回滚。

    这个很好理解,在数据库事务中我们常常遇到这种情况,事务未提交前是可以读到与已提交数据不同的值的。对于系统执行事务来说,在事务执行过程中,系统其实处于一个不一致的状态,不同的节点的数据并不完全一致,因此第一版的解释“All nodes see the same data at the same time”是不严谨的。而第二版强调 client 读操作能够获取最新的写结果就没有问题,因为事务在执行过程中,client 是无法读取到未提交的数据的,只有等到事务提交后,client 才能读取到事务写入的数据,而如果事务失败则会进行回滚,client 也不会读取到事务中间写入的数据。

    ACID中的一致性

    前文说过CAP的一致性与ACID的一致性不是一个东西,这里补充讲讲

    ACID 是数据库管理系统为了保证事务的正确性而提出来的一个理论,ACID 包含四个约束。

    1. Atomicity(原子性)

    一个事务中的所有操作,要么全部完成,要么全部不完成,不会在中间某个环节结束。事务在执行过程中发生错误,会被回滚到事务开始前的状态,就像这个事务从来没有执行过一样。

    1. Consistency(一致性)

    在事务开始之前和事务结束以后,数据库的完整性没有被破坏。
    3. Isolation(隔离性)

    数据库允许多个并发事务同时对数据进行读写和修改的能力。隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)。
    4. Durability(持久性)

    事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。

    可以看到,ACID 中的 A(Atomicity)和 CAP 中的 A(Availability)意义完全不同,而 ACID 中的 C 和 CAP 中的 C 名称虽然都是一致性,但含义也完全不一样。ACID 中的 C 是指数据库的数据完整性,而 CAP 中的 C 是指分布式节点中的数据一致性。ACID 的应用场景是数据库事务,CAP 关注的是分布式系统数据读写这个差异点。

    可用性

    直接来看第一版对可用性的定义

    Every request gets a response on success/failure.

    简单翻译为:每个请求都能得到成功或者失败的响应。

    这个描述太模糊了,比如我们在上文提到了不同节点数据是可能不一致的,比如两个节点之间网络不通,那么此时系统可以选择直接返回错误信息或者旧数据,那么此时该请求这个算成功还是失败呢。所以我们来看第二版对可用性的定义

    A non-failing node will return a reasonable response within a reasonable amount of time (no error or timeout).

    翻译下:非故障节点将在合理的时间内(无错误或超时)返回合理的响应。

    这个就比较清晰了,如果节点宕机了,不在讨论范围内,如果节点错误或超时了,那么就是不可用,至于返回是新数据还是旧数据,只要能返回数据就可以,因为第二版没有强调必须是正确的数据,如果返回的旧数据,肯定是不正确的结果,但可以是一个合理的结果。

    我们在来看看上面说的故障是指什么故障,一般情况下,我们讨论的故障是指节点宕机或无法响应,而不是分区故障。

    分区故障:以网络分区为例,网络分区是指因为网络故障导致网络不连通,不同节点分布在不同的子网络中,各个子网络内网络正常。其实,你可以这么理解,节点之间的网络通讯出现了消息丢失、高延迟的问题。

    所以说可用性说的是任何来自客户端的请求,不管访问哪个非故障节点,都能得到响应数据,但不保证是同一份最新数据。你也可以把可用性看作是分布式系统对访问本系统的客户端的另外一种承诺:我尽力给你返回数据,不会不响应你,但是我不保证每个节点给你的数据都是最新的。

    举个例子

    假设下图两个节点都是正常工作的,并且可以互相通信,但是这两个节点数据同步是异步的,所以数据不一致,但是如果客户端请求来了两个节点都返回数据,此时,有一个节点数据是旧的,这不是正确的结果,但是是合理的响应,所以这种情况是满足可用性的

    再举个例子

    来看下面的图,当节点 1 和节点 2 通信出问题的时候,数据无法同步,但是如果两个节点都能响应客户端,我们也说这是满足可用性的。

    分区容错性

    第一版解释:

    System continues to work despite message loss or partial failure.

    简单翻译为:出现消息丢失或者分区错误时系统能够继续运行。

    第二版解释:

    The system will continue to function when network partitions occur.

    简单翻译为:当出现网络分区后,系统能够继续“履行职责”。

    第一版解释和第二版解释主要差异点表现在:

    • 第一版用的是 work,第二版用的是 function。

    work 强调“运行”,只要系统不宕机,我们都可以说系统在 work,返回错误也是 work,拒绝服务也是 work;而 function 强调“发挥作用”“履行职责”,这点和可用性是一脉相承的。也就是说,只有返回 reasonable response 才是 function。相比之下,第二版解释更加明确。

    • 第一版描述分区用的是 message loss or partial failure,第二版直接用 network partitions。

    对比两版解释,第一版是直接说原因,即 message loss 造成了分区,但 message loss 的定义有点狭隘,因为通常我们说的 message loss(丢包),只是网络故障中的一种;第二版直接说现象,即发生了分区现象,不管是什么原因,可能是丢包,也可能是连接中断,还可能是拥塞,只要导致了网络分区,就通通算在里面。

    不过集群毕竟不是单机,当发生分区故障的时候,有时不能仅仅因为节点间出现了通讯问题,无法响应最新写入的数据,之后在客户端查询数据时,就一直返回给客户端出错信息。

    分区容错性说的是,当节点间出现任意数量的消息丢失或高延迟的时候,系统仍然在继续工作。也就是说,分布式系统在告诉访问本系统的客户端:不管我的内部出现什么样的数据同步问题,我会一直运行。这个指标,强调的是集群对分区故障的容错能力。

    其实就是上一张图,假设他是发生了网络分区,节点与节点之间无法通信,但节点与客户端之间可以互相通信。在此基础上,如果这节点2返回了数据,而不是拒绝响应或者超时,通过可用性的分析我们知道下图这个系统是满足可用性的,并且是在分区故障的时候满足可用性的,那么我们可以说这个系统又满足了分区容错性,但是很明显不满足一致性,所以这个系统是AP模型。

    讲到这里,你可能有点懵,上面说分区容错性指的是在分区故障情况下系统依然工作,看上面的例子,这不必然带着可用性了么,那么CAP的P,说的是发生了分区故障还是发生了分区故障后还能工作?我相信如果将CAP中的P描述为分区故障你更好理解,但实际上并不是,你可以把发生了分区故障后还能工作(分区容错性)看成目标,C和A是手段。当发生分区故障的时候,要么你保证可用性,但是数据肯定不一致;要么你保证数据一致,但是可用性必然无法保证(因为数据无法同步到其他分区节点,其他分区节点不可用)。 这类似于ACID,数据库事务中的ACID中的C(一致性)是目标,AID是手段。而CAP中P是目标,C和A是手段。

    下面详细讲讲

    可用性与分区容错性,傻傻分不清

    一致性大家比较好理解,多个数据节点保持数据一致即可,实现方式上就是更新时所有数据节点都更新成功才返回更新成功。

    主要是可用性和分区容错性比较难以理解,下面详细说说。

    问题1:分区容错性说分区故障正常工作,什么叫正常工作?这个正常工作是指满足可用性吗?

    可能有一部分人被我上面的图(上一张图)误导了,觉得分区容错性和可用性是绑定在一起的,其实并不是,来看下一张图。相比上一张图,这张图的节点2与客户端不通了,当然这不是网络不通,而是客户端被选择的结果,这个选择就是如果节点2出问题了,没有最新的数据,那么就让客户端不访问节点2,而是访问节点1,这样系统是不是就保证数据一致性了,但是很明显不满足可用性了,我节点2没有宕机,没有断网,客户端你可以访问我但是你不访问,你为了拿到最新的数据,保证数据一致性,舍弃了节点2。

    此时,该系统是正常工作的吗,是。该系统发生故障了吗?发生了。该系统满足可用性了吗?没有保证。我们可以回答上面的问题了,分区容错性说的是发生分区故障的时候正常工作,这个正常工作,并不是可用性,当我们舍弃可用性,保证数据一致性的时候,数据也是正常工作。

    注意,可用性并不是说系统能工作就叫可用性,而是某个分区中的节点和客户端可以正常工作。

    问题2:为什么说分区容错性是目标,而CA要舍弃一个,不能设计CA系统,舍弃P吗
    1. 什么叫分布式系统?多个节点,网络通信
    2. 网络通信是不可靠的,tcp也一样,参考两军问题
    3. 分区容错虽然不是时刻发生,但他意味着必然发生,因为网络是不可靠的

    所以分区容错性是目标,是分布式系统必须要保证的,不能因为部分节点网络问题导致整个系统不能工作。

    因为分布式系统与单机系统不同,它涉及到多节点间的通讯和交互,只要有网络交互就一定会有延迟和数据丢失,而这种状况我们必须接受,还必须保证系统不能挂掉。所以就像我上面提到的,节点间的分区故障是必然发生的。也就是说,分区容错性(P)是前提,是必须要保证的。

    假设一个分布式系统我要保证CA,当发生分区容错的时候,为了保证数据一致性,多个节点数据无法同步,那么我更新操作是等待所有节点更新成功才返回更新成功呢,如果这样那么这个更新操作永远不能成功,再假设我不等待所有节点都更新成功就返回,我保证我读的时候去拿最新的数据,如果我发起一个读请求,发现他不是最新的,我要等待最新的数据同步过来,可是已经发生分区故障,我永远等不到最新数据过来,最后只能超时报错。所以说,当发生分区故障的时候,C和A无法同时保证。

    问题3:C和A真的无法共存吗

    当然不是,前后强调了很多次了,只有当发生分区故障的时候,C和A无法同时保证。不发生分区故障的时候,节点与节点之间,节点与客户端之间都能正常通信,那么这个系统此时肯定是既满足一致性又满足可用性。

    至于我们说的CAP中的P,虽然说当发生分区故障的时候什么什么,但是由于网络分区是必然会发生的,同时分区容错性也是必须保证的,所以我们设计系统时必须假定已经发生了分区故障。

    当然,如果是单机系统,没有网络分区的可能,那么自然是满足CA的,但这个系统也不叫分布式系统了,没必要讨论。

    所以我们要知道,C和A无法共存指的是发生分区故障的时候无法共存,网络正常两个都可以满足的。

    是CAP不能共存吗,是的,准确的说,是分区故障导致了CA不能共存

    总结

    CAP的理论终于讲完了。

    这里就不重复CAP的定义了,我觉得理解CAP的关键点有以下几个:

    • CAP探讨的分布式系统是副本型的分布式系统。比如Mysql集群,有了实际例子就很好理解
    • 由于网络不可靠分区故障是必然会发生的,所以在分布式系统中P一定要保证
    • 系统可用(正常工作)和CAP说的可用性不一样,前者站在客户端的角度,后者站在分区节点的角度
    • 分布式系统中C和A可以共存,但无法一直共存,当发生分区故障时必须要牺牲一个,不发生的时候两个都能保证

    一点拙见,主要内容还是参考了大佬的文章,这里添加了一些对于新手比较容易产生的问题并尝试解答,希望对你有所帮助

    参考

    极客时间-从0开始学架构
    -李运华

    极客时间-分布式协议与算法实战
    -韩健

  • 相关阅读:
    HDU 1165 Eddy's research II (推公式)
    HDU 1394 Minimum Inversion Number (线段树&&暴力)
    HDU 2845 Beans (最大不连续子序列和)
    CodeForces 369A Valera and Plates( 水)
    HDU 1241 Oil Deposits(dfs)
    hdu 1016 Prime Ring Problem(dfs)
    hdu 5138 CET-6 test(水)
    ZOJ 3693 Happy Great BG(卡精度)
    HDU 1028 Ignatius and the Princess III(dp 母函数)
    CodeForces 432B Football Kit(水)
  • 原文地址:https://www.cnblogs.com/lifan1998/p/14866466.html
Copyright © 2011-2022 走看看