附录B CAP定理

理解5种数据库类型是一个重要的选择标准,但并不是唯一的标准。在本书中另一个反复出现的主题是CAP定理,它揭示了一个令人不安的事实,即面对网络的不稳定性,分布式数据库系统如何表现。

CAP证明了,可以创建一个分布式数据库,它是一致的(写入是原子的而且所有后续请求检索出新的值),可用的(只要一台服务器在运行,数据库将始终返回值),或者分区容错的(即使服务器的通信暂时中断了,系统仍将运行——那就是,网络分区),但是你只能同时拥有以上特性中的两个。

换句话说,可以创建一个分布式数据库系统,它是一致的和分区容错的,系统是可用的和分区容错的,或系统是一致的和可用的(但不是分区容错的,这基本上意味着不是分布的)。但是不可能创建一个分布式数据库,它是一致的、可用的,同时也是分区容错的。

CAP定理在考虑一个分布式数据库时是有用的,因为你必须决定你愿意放弃什么。你选择的数据库将失去可用性或一致性。分区容错是严格意义上的架构决策(数据库是否是分布的)。重要的是要了解CAP定理以充分把握你的选择。在本书中对数据库实现的取舍在很大程度上受CAP定理影响。

B.1 最终一致性

分布式数据库必须是分区容错的,所以在可用性和一致性之间的选择很困难。然而,CAP指出,如果你选择了可用性,你就不能拥有真正的一致性,你仍然可以提供最终一致性。

CAP冒险,第一部分:CAP

将世界想象为一个巨大的分布式数据库系统。在世界上的所有陆地上包含关于某些主题的信息,只要你在人或技术的附近,你可以找到你问题的答案。

现在,为了论证,想象你是碧昂丝·诺尔斯(Beyonce Knowles)的一个铁杆粉丝,而日期是2006年9月5日。突然,在你朋友的海滨别墅庆祝碧昂丝第二张录音专辑发行的聚会上,一个奇怪的波浪席卷了码头,并把你拖到了海里。你做了一个临时的筏子并在几天后被冲到了一个荒岛上。没有任何的通信手段,你实际上与系统(整个世界)的其他部分隔离开来。在那里你等了长长的五年……

在2011年的一个早晨,你被来自海上的喊声惊醒。一位富有经验的纵帆船老船长发现了你!在你单独一人五年之后,船长弯腰大声问道:“碧昂丝有多少张录音专辑?”

你现在要作一个决定。你可以用你所拥有的最近(现在是五年前)的值回答这个问题。如果你回答他的问题,你是可用的。或者,你可以拒绝回答这个问题,因为你知道你被隔离了,你的回答可能无法与世界其他地方保持一致。船长不会得到回答,但世界的状态保持了一致(如果他驾船回家,他就可以得到正确的答案)。你作为被查询的节点,可以帮助保持世界数据的一致或可用,但不能同时两者兼备。

最终一致性背后的思想是,每个节点始终可用于服务请求。作为一个权衡,数据修改在后台被传播到其他节点。这意味着,在任何时候,系统可能会不一致,但是大体上数据仍是准确的。

互联网域名服务(Domain Name Service,DNS)是最终一致性的一个典型例子。你注册一个域名,可能需要几天时间传播到在互联网上的所有DNS服务器。但是没有任何时候任何特定的DNS服务器是不可用的(假设你可以连接到它,那它就是可用的)。

B.2 实际中的CAP

一些分区容错的数据库可以调整为对每个请求或多或少一致或可用。Riak的运行就像这样,允许客户端在请求时决定它们需要什么程度的一致性。本书中的其他数据库在很大程度上占据了CAP权衡三角形的某一个角。

CAP冒险,第二部分:最终一致性

让我们回到两年前,2009年。在这个时间点上,你已经在岛上待了三年,你在沙子中发现了一个瓶子,这是与外界的宝贵接触。你拔去瓶塞,欢欣鼓舞!你刚刚获得一项完整的知识……

碧昂丝录音专辑的数量对世界的总知识是至关重要的。它是如此重要,事实上,她每次发行新专辑,就有人在纸上写上当前日期和数量。他们把这张纸放到一个瓶子里,并把它扔到海里。如果有人,像你自己一样,在一个荒岛上与世隔绝,他们最终能得到正确的答案。

向前跳转到现在。当船长问你:“碧昂丝有多少张录音专辑?”你仍然可用并回答:“三张。”你可能与世界其他地方是不一致的,但在尚未收到另一个瓶子之前,你对你的答案相当肯定。

故事的结局是,船长救了你,你回到了家,发现了碧昂丝的新专辑,并从此过上了幸福的生活。只要你留在陆地上,你不需要分区容错,并能保持一致性和可用性,直到你的生命结束。

Redis、PostgreSQL和Neo4j是一致的和可用的(CA);它们不分布数据,因此分区不是一个问题(虽然可以说,CAP在非分布式系统中并没有多大意义)。MongoDB和HBase一般是一致的和分区容错的(CP)。在网络分区的情况下,它们可能无法回应特定类型的查询(例如,在Mongo副本中设置标记slaveok为false,用于读取)。在实践中,完善地处理硬件故障——其他仍然联网的节点可以为宕机的服务器提供后备支持——但是严格说来,在CAP定理的意义中,它们是不可用的。最后,即使两个或更多的CouchDB服务器可以在它们之间复制数据,CouchDB不保证任意两个服务器之间的一致性。

值得注意的是,这些数据库中的大部分可以通过配置改变其 CAP 类型(Mongo 可以是CA,CouchDB可以是CP),但在这里,我们已经注意到它们的默认或通常行为。

B.3 延迟权衡

然而,分布式数据库系统的设计不仅仅需要CAP。例如,低延迟(速度)是许多架构师的主要关注。如果你读过亚马逊的Dynamo1论文,你会注意到很多关于可用性和亚马逊的延迟要求的讨论。对于特定类型的应用,即便是很小的延迟变化也可能转化为一个大的成本。著名的例子是,雅虎的PNUTS数据库放弃了正常运行的可用性和分区上的一致性,从而在设计上挤压出低延迟2。重要的是,在与分布式数据库打交道时要考虑CAP,但同样重要的是要知道,分布式数据库理论并不止于此。

注释1 http://allthingsdistributed.com/files/amazon-dynamo-sosp2007.pdf。

注释2 http://dbmsmusings.blogspot.com/2010/04/problems-with-cap-and-yahoos-little.html。