CAP迷思:关于分区容忍性
CAP1理论是分布式存储系统的热门话题。然而,它被大量误用了。在本文中,我想强调为什么通常的说法“一致性(C),可用性(A)和分区容忍性(P),只能选择两个”,对分布式系统来说,是不适当的。事实上,我们从CAP理论中学到的是,只能在顺序一致性和高可用性中选择一个。
(备注:作者的意思是,只能CP或者AP,因为P必选,所以说只能从C和A中选一个)
在设计分布式存储系统或者谈论它的设计时,常常提到CAP理论。按照该理论通常的表述,设计者必须在三个相互排斥的条件中作出选择。
- 一致性:大意是存储系统的所有客户端请求,都能得到一个“说的过去”的响应。例如:A先写入1再写入2,B不能读到2之后又读到1.
- 可用性:存储系统的所有操作最终都返回成功。我们称系统是可用的。
- 分区容忍性:如果集群中的机器被分成了两部分,这两部分不能互相通信,系统是否能继续正常工作。
这通常被概述为一句话“一致性,可用性,分区容忍性,只能选择两个”,简单,干脆,实用。
至少,传统观点是这样的。很多现代分布式存储,包括NoSQL,以提供可用性和分区容忍性,而不是强一致性,而自豪。它们认为,和短时间的不可用相比,短时间的应用异常要更好些。的确,Stonebraker博士在ACM的博客上,为多数系统选择了AP而感到遗憾,并且认为一致性和可用性才是应该选择的。然而,对绝大多数系统而言,我坚决主张,只能在一致性和可用性之间做选择。
Stonebraker博士的核心观点是,分区很罕见,我们可以简单的牺牲掉“分区容忍性”,选择顺序一致性和可用性,这个模型很适合传统的事务处理,能保持多数关系数据库的ACID不变。我想说明,为什么这是对CAP理论的误读。
我们先弄明白“分区容忍性”的确切含义是什么?Stonebraker博士断言,如果网络故障时,分区两边的处理可以继续,那就是分区容忍的。
Seth Gilbert 和 Lynch在他们的论文中提供了CAP理论的公式和证明。我们应该参考他们对分区容忍性的定义,如果我们想把CAP当作一个数学真理来引用,我们就应该公式化我们的基础,否则它就是建立不稳固的基础上的。Gilbert和Lynch是这样定义分区容忍性的:网络允许丢失以一个节点发给另一个节点的任意多的消息。
注意,Gilbert 和 Lynch 定义的不是分布式应用的性质,而是应用所在的网络的性质。分区容忍性不是我们设计系统时可以选择的东西(没得选)。如果网络发生分区,你或者失去一致性(因为允许分别更新分区两边)或者失去可用性(因为你检测到错误,关闭系统,直到分区消失)。分区容忍性意味着,开发一个应对策略,当分区时,丢弃一致性或可用性。这时我们真正从CAP中学到的,当你有个会丢消息的网络,你不能同时拥有可用性和一致性。
Stonebraker关于分区容忍性的定义实际上是衡量可用性的,如果写到任何一个分区,最终会不会有响应?这对跨越不同地理位置的系统很有意义,但是在局域网中,两个分区都可写并不常见。然而,它是隐含在可用性要求中的,如果系统总是可写的,那么在网络分区时,当然也是可写的。
那么什么导致了分区?有两个。第一个很显然,网络故障,例如交换机坏了,可以导致网络分区。第二个没那么明显,但是符合Gilbert和Lynch对分区的定义,机器故障,无论是软件的,还是硬件的。例如在异步网络中,消息处理没有在指定时间完成,不可能区分是机器故障还是消息丢失了。这样,一台故障机器自个和其余机器发生了分区。相关错误发生在一组机器,这组机器就和其它机器发生了分区。不能收到消息和网络没有投递消息是等价的。当足够多的机器故障时,保持可用性和一致性(中的一个)是不可能,不是因为写到了不同的分区,而是机器数量达不到的规定的数目(quorum机制), 可能导致最近的写没法读出来。
(备注:这就是为啥分区容忍是必选的,因为无论如何,它都要发生,无法控制)
这就是为啥,把P定义为“允许分区保持可用”是有误导性的,机器失败是分区,当他们失败时系统可能不可用。Stonebrake还说他建议选择CA而不是P。这有点像同时要熊掌和鱼。不选择P,类似于要建一个永远不发生多相关故障的网络。正是因为Stonebrake在博客中描述的原因,相关错误,OS bug和集群灾难等,对分布式系统来说,构建这样的网络是不切实际的。所以设计者必须在一致性和可用性之间做选择。Stonebrake告诉我们要选择一致性。事实上,当大的失败事件发生时,可用性不可避免的要受到影响。这是合理的选择,RDBMS族的数据库把它用到了极致。但它既不能使我们免于小故障带来的可用性问题,也不能使我们免于花大力气维护顺序一致性。
当机器扩展到成百上千的规模时,面对可能的错误,维持一致性变的非常昂贵(你不得不往更多的机器写数据,机器数目比你所能容忍的失败机器多一)。有一个细微的区别,CAP理论没说,考虑到吞吐量和延迟,维护一致性比维护可用性代价更高。Zookeeper是顺序一致的,因为集群规模够小,把数据写入法定多数的机器代价相对较小。HDFS也选择了一致性,如果倒霉,三个数据节点故障就导致文件块儿不可用。两个系统都设计为,能在普通的网络环境运行,当分区和失败发生时,他们可能变得不可用,在一致性和可用性间选择了一致性。对分布式系统来说,这种选择是不可避免的。
Footnotes:
:本文译自 cap confusion problems with partition tolerance。文章的评论也有意思,CAP理论是愚蠢的,分区是可以避免的。分区发生时,通过把请求导入到最大的分区,可以移除分区,实现CAP。我个人认为大部分时候CAP理论的确是愚蠢的,大部分NoSQL实现在分区(节点故障)时仍然是高可用和一致的,但不绝对保证,因为节点故障是失败-恢复模型,不是失败-停止模型,前者在发生在节点应用故障或机器负载高,暂时不可用时,而后者发生在机器宕机,长时间稳定不可用时,显然前者更常见,不然就不需要Paxos,3PC就够了