跳转到主要内容
Dan 提交于 21 November 2012

事务(Transaction)的典型例子是汇款给别人。这是一个极为复杂的过程,但是在当天结束的时候,你期望你的帐户余额减少了你汇出的金额,而收款人的余额增加了该金额(减去汇费)。你同样期望,一旦银行说“你汇出了钞票”,它就确实汇出了,而不管实际的汇款需要用时多久(有个叫作SWIFT的汇款耗时简直是长得太荒谬了)。还有一个期望是,如果钱从你的帐户中消失了,它就会出现另一方的帐户中,而不管银行的计算机系统出了什么状况。你当然不想如果某台电脑崩溃,你的钱就消失得无影无踪了。基于这些期望,数据库的一组属性集合足以使事务正常工作——它们一般缩写为ACID:

  • 原子性(Atomic:):要么整个事务成功,要么整个不成功。
  • 一致性(Consistency):数据库在事务之间处于一个一致的状态中。比方说,如果一条记录指向另一条记录,而到事务结束时这个指向是无效的,那么整个事务就必须回滚。
  • 隔离性(Isolation):在其他事务结束之前,事务看不到被它们更改的数据。
  • 持久性(Durability):一旦数据库系统通知用户事务成功,数据就永不丢失。

另一方面,大部分的Web应用程序需要的是完全不同的一组属性,它们被巧妙地命名为BASE:(BASE更常见的解释是:Basically Available, Soft-state, Eventual consistency,和本文此处所述略有出入。——译者注。)

  • 基本可用(Basically Available):用户都有一种愚蠢的期望,就是当他们把浏览器指向一个网页时,就会有某些信息出现。这是你期望发生的,即便系统的某些部分宕机了也一样。这听上去微不足道,事实却并非如此;有很多系统只要一台服务器宕了,整个系统就宕了。
  • 可伸缩(Scalable):添加更多的服务器使得服务更多的客户成为可能。不是创建一个巨大的怪物服务器,而是添加更多的服务器,这更具有前瞻性,而且也通常更便宜。重申一下,这是用户的期望之一:信息不仅仅是出现,而且还要快速地出现。(注意这项用户期望不仅要求伸缩性,还同时要求性能。)
  • 最终一致(Eventually Consistent):如果数据最终在所有副本出现的地方变为可用,这就足够了(如上一点所述,你可以有很多副本)。这里的期望是,信息要快速出现才会显得及时。 当你在Craigslist上发布一个广告,它不会马上出现在列表和搜索结果中,这不是太大的问题;但如果一个广告需要几天才出现,就没有人会去用那个网站了。

关于这里的最后一点,如果你有一个像ACID属性列表那样的一致性系统,它也是“最终一致的”,因为它遵循一个更强的要求:数据立即对所有副本可用。(这一点我会稍后回来详细讲解。)

如果有个数据库系统能够既是ACID又是BASE,那就太棒了,可是,唉,那其实是不可能的。记住,BASE一般与由大量服务器组成的系统有关。如果你要求所有的写入都非常一致,那么每次写入都要经过每台服务器,而你则必须等到所有的服务器都完成写入并且回应这个事实。现在,如果你有分布在全球各个数据中心的服务器,那么网络问题是意料之中的。让我们假设有一个横跨大西洋的连接发生故障——这叫作网络分区。如果你需要一致性,那么系统就必须和横跨大西洋的连接一起发生故障——即使其它所有的服务器都是工作的——因为在欧洲的写入无法到达美国,反之亦然,所以那些部分将产生不一致数据。你需要放弃一些东西:要么放弃BASE中的可用性(A),系统宕机以获取一致性;要么放弃ACID中的一致性(C),你屹立不倒而系统不一致。换句话说,在一致性、可用性和分区宽容度这三者中,你只能选择两者而不能三者兼而有之。至此,我只是为数据库定义过一致性这个术语,所以是时候来给它一个一般性的定义了:在任何时间点,不管哪台服务器应答了一个请求,所有服务器都会给出同样的答案。可用性,如上所述,意思是即使某些服务器宕机了,整个系统仍能正常工作。最后,分区宽容度是指两台服务器之间的通信可以丢失,而系统仍能正常工作。你看后面两点,尽管多么“软性”,但当你需要它们和一致性并存时,它们又摆出一副很难搞定的样子了。

我已经展示了CAP的三个部分不能同时工作,而我也可以展示它们中的任何两个都可以轻松共存。如果你确保网络的各部分永不失效,那么提供一致性和可用性就不是问题。尽管听上去不大可能,Google的BigTable实际上正是一个这样的系统,有超过60个Google项目在使用它。如果你抛开可用性,那么另外两个需求也能轻松满足:只要你不打开宕掉的服务器,即使部分网络停止工作了,数据还是会保持一致。最后,如果你不关心一致性,你大可不必从一台服务器往另一台服务器复制数据——这种系统自然不必关心网络分区(因为它根本就没有用到网络),并且只要你到达一个工作的服务器,它总能应答一些东西。虽然后面两个例子有点极端,不能用来描述一个有用的系统,但是这里要重申的是,尽管一致性、可用性和分区宽容度三者不能共存,但它们中的任何两者都可以。

让我们重新回忆一下咖啡店的故事:为了能够雇佣更多的咖啡师(规模化),你需要接受顾客在短时间内付了钱而没有咖啡这个事实(最终一致而不是马上一致)。而且人们甚至在对待Web应用程序时也接受这点。如果你的评论需要过一会儿(即使是几分钟)才能显示给全世界每个人,并无伤大雅。当然,你期望它能马上显示给评论的发布者,或者至少在数据一旦发往服务器并且回复已经到达的时候。但是对于其他人,从互联网上加载网页总是要耗费一些时间的,所以,对于那些刚开始加载页面的人们来说,服务器是在“发送”按钮按下前还是按下后显示那个新的评论,几乎无关紧要。更重要的是网页总能响应(可用的),而不管有多少人在同时浏览(伸缩性)。

所以,虽然SQL数据库大多是兼容ACID的,但注重BASE的数据库更适合Web应用程序。不仅如此,下面的这些数据库还把翻天覆地的硬件变化和操作系统的可能性考虑其中,以形成更加适应这些目的的数据库版本。依据这些理论而构建的数据库先驱之一是CouchDB,发布于2005年,Cassandra于2008年向公众发布,而MongoDB首次发布于2009年。

同样是在2009年,这些个新的非关系数据库系统们有了一个琅琅上口的别号——“NoSQL”。当然这只是个流行词,因为用SQL来驱动一个不兼容ACID的数据库也是可能的(实际上MySQL就在不兼容ACID的情况下使用SQL有十多年了),但是这些数据库的特性集和SQL所要求的是如此不同,因而没有意义去尝试。比方说,这些数据库中没有一个提供数据表联接(JOIN)功能。

由于MongoDB是很多Drupal任务的最佳选择,我现在就来对它进行详细讨论。