理解CAP定理:系统设计中的一致性、可用性与分区容错性
1999年,埃里克·布鲁尔提出了一种理论,这一理论在随后几十年里极大地影响了分布式系统的设计方式。他指出,任何分布式数据存储系统最多只能同时满足三个属性中的两个:一致性、可用性和分区容错性。 两年后,塞思·吉尔伯特和南希·林奇正式证明了这一观点,这一理论后来被称为CAP定理。 所有那些需要在多台机器上存储数据的分布式数据库、云服务或系统,都必须在这三个属性之间做出选择。理解这些属性的具体含义、为什么不能同时满足全部三个属性,以及在实际应用中应该如何做出这种选择,对于任何负责构建或运营大规模系统的工程师来说,都是至关重要的基础知识。 目录 先决条件 三个属性 为什么不能同时满足全部三个属性 真正
1999年,埃里克·布鲁尔提出了一种理论,这一理论在随后几十年里极大地影响了分布式系统的设计方式。他指出,任何分布式数据存储系统最多只能同时满足三个属性中的两个:一致性、可用性和分区容错性。
两年后,塞思·吉尔伯特和南希·林奇正式证明了这一观点,这一理论后来被称为CAP定理。
所有那些需要在多台机器上存储数据的分布式数据库、云服务或系统,都必须在这三个属性之间做出选择。理解这些属性的具体含义、为什么不能同时满足全部三个属性,以及在实际应用中应该如何做出这种选择,对于任何负责构建或运营大规模系统的工程师来说,都是至关重要的基础知识。
目录
先决条件
在阅读本文之前,您需要掌握以下基础知识:
什么是数据库,以及读写数据的基本概念
数据存储在多台机器上的含义(从概念层面理解复制机制)
基本的网络知识:什么是网络请求,两台机器如何进行通信
您不需要具备分布式系统的使用经验,因为本文会从基础原理开始逐步讲解这些概念。
三个属性
一致性
在CAP定理的框架下,“一致性”具有特定的技术含义,这与日常对话中使用这个词的方式是不同的。
在CAP体系中,一致性意味着每次读取操作都会得到最新的数据,要么得到正确的数据,要么收到错误提示;绝不会得到过时的数据。
如果您向一个具备一致性的分布式系统写入某个值,然后立即从该系统中的任意节点读取这个值,您得到的肯定就是您刚刚写入的那个值。集群中的每个节点在任何时刻都保持着相同的状态。
这种特性被称为“强一致性”或“线性化能力”,它是一种严格的保障机制。如果节点A拥有而节点B还没有某个数据,那么一个具备一致性的系统要么会等到节点B更新了该数据后再响应读取请求,要么会拒绝响应直到数据一致性得到恢复为止。
在CAP理论中,“一致性”这个概念与ACID模型中的“C”所代表的含义并不相同。ACID模型中的“一致性”指的是数据完整性约束,而CAP理论中的“一致性”则意味着所有节点必须同时看到相同的数据。
可用性
可用性意味着每个请求都会得到响应。不一定是最新数据,但至少会收到某种回应。系统永远不会拒绝处理请求,也永远不会因为暂时无法处理查询而返回错误信息。
一个具备可用性的系统,即使其中某些节点出现故障或无法被访问,也会继续响应用户的请求。该系统更重视系统的正常运行时间,而非数据的实时性。如果节点A无法与节点B通信以获取最新数据,那么这个可用性系统仍然会使用自己拥有的数据进行响应,即便这些数据已经稍微过时了。
从用户的角度来看,这样的系统始终处于“在线”状态,用户的请求也总能得到回应。
分区容错性
当分布式系统中节点之间的通信链接中断时,就会发生网络分区。此时节点A无法与节点B进行通信——尽管这两个节点都在正常运行且没有故障,但它们之间却无法互相交流。
分区容错性意味着系统在遇到网络分区的情况下仍能继续运行。即使两个节点无法通信,系统也不会完全停止工作。
正是这种特性使得CAP理论变得如此重要。网络分区并不是理论上的概念,在实际的生产系统中这种情况经常发生:网络电缆可能会损坏,路由器会丢包,数据中心也会失去连接能力,云服务提供商也可能出现故障,导致不同区域之间的系统无法互相通信。任何跨多台机器运行的系统都必须面对这样一个现实:这些机器有时会失去相互通信的能力。
为什么不可能同时满足三个目标
以下这个场景可以用来证明这一结论:
假设你有一个由两个节点组成的分布式数据库:节点A和节点B。这两个节点会互相复制数据。此时发生了网络分区,节点A和节点B无法再相互通信。
如果有一个写操作被发送到节点A,节点A会处理这个操作并存储新的数据值,而节点B仍然保留着旧的数据值。此时这两个节点的数据已经不同步了,而且它们也无法通过通信来恢复同步状态。
如果此时有一个读操作被发送到节点B:
如果你选择“一致性”: 节点B会意识到自己无法与节点A通信,因此它知道自己持有的数据可能是过时的。为了确保每次读取操作都能得到最新的数据,节点B必须拒绝响应,直到它能够与节点A重新同步为止。这种情况下,系统虽然保持了数据的一致性,但可用性却受到了影响。 如果你选择“可用性”: 节点B会使用自己持有的数据进行响应,即使这些数据可能是过时的。虽然系统仍然可以正常运行,但读取到的数据可能并不反映节点A中的最新数据。这种情况下,系统的可用性得到了保障,但一致性却无法保证。实际上并没有第三种选择。在发生网络分区的情况下,分布式系统必须在这两个选项之间做出抉择:要么返回可能已经过时的数据(这样系统是可用的,但不一定保持数据的一致性),要么拒绝响应用户的请求(这样系统可以保持数据的一致性,但可用性就会受到影响)。
在这种情况下,“分区容错性”其实并不是一种可供选择的选项。如果你的系统运行在通过网络连接的多台机器上,那么分区的现象就会发生。你可以选择应对这些分区问题,从而实现分区容错;或者选择不采取任何措施,这样一来,一旦发生分区,你的系统就会直接停止运行。
大多数实际应用的系统都无法承受直接停止运行的后果。因此,对于任何需要保持正常运行的分布式系统来说,“分区容错性”实际上是一种必备的功能。
正因如此,在分布式系统中,真正需要做出的选择是在“分区发生时保持数据一致性”与“确保系统的可用性”之间进行权衡,而不是在这三个选项中自由选择。
真正的选择:CP还是AP
CP系统:一致性优先于可用性
CP系统会在发生分区时选择保持数据的一致性,但这样就会牺牲系统的可用性。当节点之间无法进行通信时,该系统会拒绝响应请求,而不是冒着返回过时数据的风险继续运行。
当正确性比系统正常运行时间更为重要时,这种选择才是正确的。对于金融系统、库存管理系统,或者任何那些提供错误数据比根本不提供数据更糟糕的应用场景来说,这种机制都是至关重要的。
举个例子:假设某个支付系统正在运行中,客户发起了一笔转账请求,但此时网络发生了分区,目标节点无法与源节点取得联系以确认转账是否完成。在这种情况下,CP系统会拒绝显示更新后的账户余额,直到它能够确认所有节点上的数据都是一致的。用户可能会遇到超时错误或其他异常提示,但他们看到的账户余额肯定会是准确的。
这种权衡是真实存在的:在分区期间,一些请求确实会失败,用户也会遇到错误信息,但最终他们看到的数据仍然是正确的。
AP系统:可用性优先于一致性
AP系统会在发生分区时选择确保系统的可用性,但这样就会牺牲数据的一致性。当节点之间无法进行通信时,该系统仍会继续响应用户的请求,即使它所提供的数据已经过时了。
当系统正常运行时间比拥有绝对最新的数据更为重要时,这种选择才是合适的。对于社交媒体信息流、产品目录展示、用户个人资料查询等功能来说,允许数据稍微滞后一些也是可以接受的。
再举个例子:在一个社交媒体平台上,当网络发生分区时,你仍然能够查看自己的信息流。AP系统会从距离你最近的可用节点那里获取内容来显示给你,即使这个节点还没有收到网络上其他节点最近几分钟发布的更新内容。你可能会暂时错过一些新帖子,但等网络恢复连接后,你的信息流就会自动补上这些内容,没有任何数据会被丢失,只是会在短时间内看到稍微过时的数据而已。
这种权衡同样是真实存在的:在不同的时间点,不同的用户可能会看到略有差异的数据状态。在分区期间写入的数据可能会导致数据冲突,而在网络恢复连接后,系统必须有相应的机制来处理这些冲突。
偏向AP模式的系统:
Cassandra、CouchDB和DynamoDB都是那些注重可用性的系统的代表。这些系统将正常运行时间视为首要目标,并采用“最终一致性”机制:即如果没有任何新的写入操作发生,所有节点最终都会达到相同的状态。这里的关键在于“最终”,而不是立即,也不是在发生网络分区的情况下,而是必然会达到一致。
Amazon DynamoDB的设计理念明确承认了这种权衡。亚马逊的购物车系统就是一个典型的例子:允许用户将商品添加到购物车中(即使各个节点上的购物车状态存在轻微差异),总比因为节点之间无法通信而拒绝用户的操作要好得多。
CA系统:特殊情况
你可能会想知道,是否存在这样一种系统:它既追求一致性,也注重可用性,但却不考虑网络分区容忍性。这样的系统必须确保自己永远不会遇到网络分区的情况。
要想完全避免网络分区,唯一的办法就是将整个系统运行在单台机器上。因为单台机器本身不可能发生网络分区。那些运行在单一服务器上的传统关系型数据库,比如独立的PostgreSQL实例,实际上都属于CA类型。这类系统具有一致性(所有读操作都能获取到最新数据)且可用性也很高(它们能够持续响应用户的请求),因为根本不存在需要处理的网络分区问题。
然而,单台机器上的数据库并不属于分布式系统。它无法实现水平扩展,也存在单一故障点。一旦将这样的数据库部署在多台机器上,它就变成了一个分布式系统,而网络分区也就成为必须面对的问题。
正因为如此,CAP定理才是专门针对分布式系统而言的。对于那些需要超越单台机器进行扩展的系统来说,CA这种设计理念在很大程度上只是一种理论上的概念而已。
实际系统及其选择
在实际应用中,分布式系统并不会简单地给自己贴上“CP”或“AP”的标签就认为问题解决了。实际情况要复杂得多。大多数系统会对不同的操作采取不同的策略,通过可调的一致性设置来配置系统的行为,并根据具体的使用场景进行优化。
Apache Cassandra
Cassandra本质上属于AP类型。它将可用性作为首要目标,采用“最终一致性”作为默认机制。不过,Cassandra允许开发人员通过设置不同的一致性级别来控制读写操作的行为。
当一致性级别设置为ALL时,所有副本都必须确认接收到了写入操作,该操作才会成功完成。这种机制能够提供强一致性,但会以牺牲可用性为代价。如果有任何副本无法被访问,写入操作就会失败。
当一致性级别设置为ONE时,只需要有一个副本确认接收到了写入操作即可。这种机制具有很高的可用性,同时也能保证最终一致性。
当一致性级别设置为QUORUM时,需要有多数副本确认接收到了写入操作。这是生产环境中最常用的设置方式,它能够在一致性和可用性之间取得平衡。
Cassandra的设计考虑到了这样一个事实:同一个系统中的不同操作可能会对一致性有不同的要求。例如,金融交易通常需要使用QUORUM级别,而分析数据的写入操作则更适合使用ONE级别。Amazon DynamoDB
DynamoDB在每次读取操作中都提供最终一致读和强一致性读两种选项。开发者可以根据具体需求来选择使用哪种方式。
最终一致读的成本更低,速度也更快;而强一致性读虽然成本更高、耗时更长,但能确保获取最新数据。
Google Spanner
Spanner是一个非常有趣的例子。谷歌开发它是为了让全球分布的系统具备强一致性。它通过原子钟、GPS接收器以及一种名为TrueTime的精心设计的协议来实现这一目标。
Spanner有效地缩小了数据中心之间事件顺序存在不确定性的范围,因此即使在跨大陆的环境中,强一致性也能得到有效保障。
Spanner挑战了“强一致性系统必须牺牲可用性”这一传统观念。但它是通过大规模的基础设施投资来实现这一目标的,并没有违反CAP定理。
ZooKeeper
ZooKeeper明确遵循强一致性原则。它被设计用于协调分布式系统中的各种操作,如分布式锁管理、领导者选举以及配置维护等。在这些应用场景中,如果返回过时的数据,将会造成严重的后果。因此,ZooKeeper愿意牺牲一定的可用性,以确保每次读取操作都能反映最新的数据状态。
CAP定理无法涵盖的所有细节
CAP定理确实是一个有用的思维模型,但它也存在一些明显的局限性,这些局限性多年来一直被分布式系统领域的专家们所讨论。
分区现象较为罕见,而延迟则是恒定的
网络分区确实会发生,但在运作良好的系统中,这种情况相对较少见。实际上,在每一个请求中、在每一个系统中,都会存在延迟——数据从一个节点复制到另一个节点所需的时间,共识协议完成执行所需的时间,以及确认所有副本都接收到了写入操作所需的时间。
CAP定理将一致性与可用性之间的选择视为一种二进制的决策,且这种决策仅发生在系统发生分区时。但实际上,工程师们总是在每次操作中都在一致性与延迟之间进行权衡,无论系统是否发生了分区。
并非所有类型的不一致性都是相同的
CAP定理将一致性视为一种二进制状态:要么每次读取操作都能返回最新的数据,要么就无法保证这一点。但实际上,从强一致性到完全的混乱之间,还存在许多不同的层次。最终一致读、单调读一致性、读写数据一致性以及因果一致性等等,这些都属于比完全没有一致性保障更强的类型。然而CAP模型并没有对这些差异进行区分。
在设计系统时应该如何考虑这些问题
在为分布式系统中的数据存储制定架构决策时,CAP定理为你提供了一个框架,帮助你提出正确的问题,而非直接给出正确的答案。
1. 首先要考虑那些真正重要的故障模式。
如果系统中的两个节点对数据的状态存在分歧,那么向用户显示错误的数据与显示错误信息,哪种情况更糟糕呢?
对于银行账户余额而言,显示错误数据显然比出现错误信息更为严重;而对于社交媒体上的浏览量统计来说,即使数值偏差几秒内,也是完全可以接受的。
2. 需要弄清楚“数据过期”对你来说意味着什么。
你的数据变化速度有多快?如果你存储的是每月只更新一次的产品描述,那么在分区期间提供五秒前的数据并不会造成任何问题;但如果你存储的是每秒钟都会变动数百次的股票价格,那么五秒的数据延迟就会成为一个严重的问题。
3> 需要考虑你的环境中分区的频率和持续时间。
如果系统运行在单个数据中心且网络环境稳定,那么分区的情况通常很少发生,且持续时间也很短;但如果系统覆盖了多个地理区域,分区就会更加频繁,持续时间也会更长。网络环境的可靠性越低,分区策略的重要性就越高。
4> 需要认识到,大多数系统实际上都需要这两种机制,只不过它们适用于不同的操作场景而已。
一个设计良好的系统会为那些必须保证正确性的操作(如金融交易、库存更新或用户认证)采用强一致性机制,而对于那些可以容忍轻微数据延迟的操作(如信息生成、数据分析或非关键性读操作),则会选择最终一致性机制。这种选择并不总是适用于整个系统,有时也会根据具体操作需求来决定。
5> 必须接受这样一个事实:这种权衡是客观存在的,无法通过技术手段完全消除。
没有任何架构技巧能够同时实现强一致性、完美的可用性以及分区容错性。那些声称找到了这样的解决方案的工程师,要么是没有仔细考虑各种故障场景,要么就是所处的环境中的约束条件比他们想象的要宽松得多。CAP定理是一个数学证明,它所描述的这些约束条件是根本性的,而非可以通过技术优化来消除的。
结论
CAP定理指出,一个分布式系统最多只能同时满足三个特性中的两个:一致性、可用性和分区容错性。由于在任何跨多台机器运行的系统中,网络分区都是不可避免的现象,因此在发生分区时,实际的选择往往是在一致性和可用性之间进行权衡。
CP型系统更注重数据的正确性,而非系统的正常运行时间。在发生分区故障时,它们会选择拒绝响应,以避免返回过时的数据。当提供错误的数据可能会造成严重后果时,这类系统才是正确的选择。
AP型系统则更重视系统的正常运行时间,而非数据的正确性。在发生分区故障时,它们会继续使用现有的数据进行响应,即便这些数据可能已经过时。当系统的可用性比拥有最新数据更为重要,且临时性的数据不一致问题可以在分区故障恢复后得到解决时,这类系统才是合适的选择。
大多数真实的分布式系统并不会做出统一的、适用于整个系统的选择。它们会提供可调节的一致性级别,针对不同的操作采取不同的策略,并根据具体的使用场景进行优化。Cassandra、DynamoDB和Spanner这些系统就分别处于这个光谱上的不同位置,而且它们都是在经过深思熟虑后才决定自己应该处于哪个位置的。
理解CAP理论的价值并不在于它能直接给出答案,而在于它能帮助你提出正确的问题:对于这些数据来说,一致性意味着什么?当节点之间存在意见分歧时会发生什么?出现错误的代价与数据过时的代价相比如何?在发生分区故障时,我的系统应该采取哪些措施?
针对你自己具体的使用场景,诚实地回答这些问题,才能为你正在构建的系统做出正确的架构决策。 相关文章
演讲主题:为人工智能代理构建数据层:从事务系统到多中心处理架构及语义模型
Fabiane Nardon介绍了TOTVS是如何为那些对大量数据“渴求不已”的AI系统准备企业数据的。她探讨了如何在精确性、安全性与成本之间平衡确定性逻辑与非确定性大语言模型。Nardon还详细说明了如何通过使用数据网格、低延迟数据库架构、语义本体以及动态的工具选择机制,来优化上下文窗口的设计,并减少事务系统中因数据处理而产生的开销。 作者:Fabiane Nardon
阅读全文
从数据到价值:通过一个实际应用案例来理解数据管理[完整书籍]
如今,数据已成为一种极其宝贵的资源。它使企业能够在市场中展开竞争,并推动创新,从而提升所提供产品与服务的质量。 数据处理能让团队自动化各种流程。它还能辅助决策制定,为终端用户带来更加个性化的体验,在诸如银行欺诈防范或风险控制等诸多领域帮助人们发现其中的规律。企业必须明白如何以有效、安全且合法的方式收集和利用数据。 你很可能目前正在使用或曾经使用过各种各样的产品与服务。你也知道,那些涉及数据的流程几乎与我们身边的所有事物都息息相关。此外,你或许已经熟悉“大数据”、“数据分析”、“人工智能”以及“机器学习”这些术语了。 但除非你是这些领域中的专家,否则其中一些概念可能会让你感到难以理解。毕竟,这些
阅读全文
借助人工智能技术,Grab Cuts公司的机械分析工作所需的时间从44%减少到了30%。
Grab正在利用人工智能技术来自动化数据分析流程,由此使得分析师所需完成的工作量从2月份的44%下降到了6月份的30%。该方案结合了人工智能系统的自主性、经过验证的数据、上下文管理机制以及人工监督功能,使得许多分析任务能够由自助分析系统来完成,而无需分析师的干预——这些系统可以自行处理各种指标数据查询及SQL请求。 作者:Leela Kumili
阅读全文
演讲主题:从模型到智能代理:在DoorDash平台上大规模开发具备上下文感知能力的消费者人工智能系统
Sudeep Das讲述了DoorDash是如何从传统的一次性预测系统转变为一个能够主动为用户提供推荐服务的平台的。他介绍了如何利用消费者与语言之间的天然联系、RQ-VAE模型生成的语义标识来优化产品信息的呈现方式,以及如何通过基于实际数据的搜索算法来显著提升推荐结果的相关性及转化率。 作者:Sudeep Das
阅读全文