今天我们来解读《Introduction to NoSQL》,这是Martin一个非常有名的视频,相信很多做过系统设计的朋友都会了解他。当然今天的话题实际也是一个非常有趣的面试题:比较SQL和NoSQL。

 

关系型数据库的兴起

 

首先来看一看历史,你能看到多远的历史,就能看到多远的未来。在20世纪80年代,关系型数据库就开始兴起了,它带来了很多有趣的内容。

 

首先是持久性:日志和备份(Log & Backup)。数据能够更有效、更长久地保存在那里,不用害怕丢失。

 

同时也带来了一致性:事务(Transaction)。不再会发生你在银行存入100块钱,结果银行显示没有存进去;或者你取出100块钱,这100块钱发生各种意外的情况。

 

还有沟通语言:SQL(Structured query language)。因为现在需要跟数据库沟通,以前跟文件沟通,只能通过文件简单地读跟写。现在有了SQL语言,能够进行一些更复杂的能力。这就是能力上的进化。

 

大家可以看到,我们学习知识的能力往往被我们语言的表达能力所局限。因此,当我们能够说出SQL语言的时候,我们跟机器沟通的能力就产生了明显的上升。同理,我们再看那个事务。以前,我们让计算器做事只能一件到一件,每一件之间是分割开的。但有了事务以后,我们就能做一些很复杂的事情。这些都是能力的提升,因此所谓的数据库其实是对文件系统的上层封装。

 

关系型数据库的产生有很多挑战,核心是“关系并非人性”。这句话什么意思呢?解释一下就是逻辑视图并非物理视图。我们看下面这个例子,左边是我们整个系统中数据的逻辑视图,比如有个ID为1001的用户,它叫Ann,有很多item,然后是有一些价钱相关的数据,以及一些支付的detail,这些整合在一起,是关于customer Ann的所有信息。但为了在关系型数据库中把它保存下来,我们需要产生很多不同的表,比如order表、customer表、每个order的每一行的一个表、以及credit card的相关信息。所以说我们的存储方式,这个物理视图,它跟我们所理解的逻辑方式也不一样,因此才会有大量的DBA(Database Administrator)产生,它们需要了解怎么能创建一个关系型数据库。所以这是个问题,因为它是非人性的。

其实在同时代我们也知道面向对象的方法,很多人就提出了面向对象的数据库,以及面向对象的思路跟底层的一个转换模型,但这些都没有火起来。有很多原因存在,但主要是由于当时关系型数据库已经统治了整个世界,想用新的数据库来占领它并不是一时能实现的。

 

随着时间的发展,我们逐渐产生了新的挑战:数据越来越大。对于数据越来越大,第一个方法是需要有更大更强的服务器,但这个并不是所有人都能承受起的。随着价钱越来越高、越来越到达我们的上限,大家开始想另外一个方法,就是更多的小服务器。首先走出来的就是Google的BigTable,还有Amazon的Dynamo。

 

NoSQL的发展

 

2009年Twitter发消息的时候,随手创建的一个tag,连到一块去成了NoSQL。所以说,NoSQL的来源只是一个巧合。

 

NoSQL是一个趋势,是我们最近十年来产生的一个很大趋势。

 

我们来看看NoSQL的分类:

             

 

这三类NoSQL数据库分类构成了聚合型数据库。什么是聚合型数据库呢?

 

对于Key-value数据库来说对应这个key的所有value都是存放在一起的。关于这个key的所有信息,都是聚合起来,并存在底层的文件上。而且在上层的内存等缓存结构中也是这样。

 

同理对于Document based,同一个document ID下的所有document也是存在一起,聚合在一起的。

 

而Column based,它通过行跟key来聚合在一起。假如说一行作为最基本的一个key,关于这行相关的所有信息也是先聚合在一起。因此,在Column based的每一行,你可以认为是一个相关的document。因此,它们全部在一起可以抽象为在某一个所谓的key的聚合下,相关的文档、或相关的信息、或相关的资料,都放在一起。因此,我们统称为聚合型数据库。

 

这个时候有什么好处呢?一个最经典的好处是,以前在SQL的结构下,我们查询这个用户数据的时候,其实底层是分散在各种不同的表里面,我们需要在不同的地方去找。而聚合型数据库的特点是,对于同一个东西的信息,只要在一个地方找就行了。

 

聚合型数据库是有结构还是无结构的呢?很多人都在说,聚合型数据库是个无结构的情况。当然我们可以说,聚合型数据库没有显式的结构,但它依然逃不了隐式的结构。很简单,无论再怎么跑,实际上永远要符合某些规则,这就是隐藏的结构。

 

聚合型数据库的产生有什么挑战呢?

 

其实最基本的挑战,是数据按照一种方式聚合,当你希望用另一种方式聚合的时候,就会产生问题。比如,有很多博客文章,首先按照文章的ID来保存信息,比如ID135下面的title、内容,包括作者,留言,都会放一个数据里面,会聚合在一起。

 

问题就来了,如果你现在想知道,某一个作者发表的所有文章,要怎么获得呢?实际上,直观来说是没有办法的,因为所有数据是按照文章ID去聚合的,而不是按照作者ID聚合。因此,这个时候要怎么办?一个最简单的方法就是MapReduce。在聚合型的数据库之上,产生MapReduce是一个很天然的想法。

 

怎么选择关系型数据库和聚合型数据库呢?

 

答案非常简单,要看你的查询是否是一种聚合方式。如果是,用聚合型数据库非常好,因为比较单一。如果是多种,需要很多种查询方式,要么选择关系型数据库,因为它可以自由组合;要不就要使用多种方式来聚合,产生各种冗余。

 

什么是一致性?

 

其实有两种一致性。一种是多个用户造成的。比如一个人在向那个文件系统中自己的账号写东西,结果另一个人也在往同一个账号里写东西,可能就会造成一个写写冲突。当然也会有读写冲突等其他方式,这就是多个用户一起往同一个地方写或读造成的冲突,叫逻辑一致性(LogicaI Consistency)

 

另外一种是副本一致性(Replica Consistency)。为了保证数据能够不丢失,往往会产生三五个副本。这些副本之间可能有一些更新有一些没更新,就会产生很多问题。之前有很多人用一致性这种方式来管理它们,现在有更好的方式即版本号的方式来管理它们。

 

什么是CAP理论?

 

CAP理论很简单,假如你有很多机器都在保存相关的数据,机器和机器之间是有副本这样的情况下,如果一些机器由于网络不通等情况被切分开来,造成了有些机器能够被访问到,有些机器不能被访问到的情况。那这个时候,为了能够更新一个数据,是要等到机器都能访问到再把数据更新了,还是说机器不用都访问到,随便找一个把它更新就可以?这样就能够很快地达到可用状态。

 

因此,当partition出现的时候怎么平衡一致性和可用性?CAP,往往并不是说三个选两个,而是说在P出现的时候,怎么平衡C跟A?当然这个可以进一步理解,比如说一致性是不是可以变成最终一致性?因为是平衡的。那可用性本质是什么?其实就是延迟。一个请求来了,需要等多久才能够获得请求。我们肯定希望延迟最少、也同时希望一致性最高。那怎么才能实现呢?大家可以想一想相关的方法。

 

举个例子,有很多人保存Amazon的购物车,那是应该优先选择数据的一致呢,还是照顾一些延迟呢?其实答案非常简单,Amazon是一个购物网站,所有人都要购物,因此做这个选择的并不是作为工程师的我们,而是产品经理或CEO。CEO说我们为了赚钱,宁愿让用户的数据不一致,也要让他们的所有购物车立刻能存下来、立刻能购买。所以他们可以要低延迟、零延迟,最后要保持一致性。

 

最后总结一下:

  • NoSQL的起源是把一个东西的数据聚集在一起。实际是在底层物理结构上把数据都存在那里。
  • CAP理论的核心是节点被分割后,如何平衡数据的一致性跟延迟性。
  • 很多用户和很多副本都会带来一致性问题

 

参考资料:《Introduction to NoSQL》Martin Fowler