自从NoSQL概念横空出世,关系数据库似乎就成了众矢之的,似乎一夜之间,关系数据库和SQL就成了低效,高成本,速度慢的数据处理模式的代名词。 在很多地方都能看到类似:”我的项目初创,应该选择什么NoSQL产品才能快速的开发?” 这样的问题。
正因有人提出这样的问题,才坚定了我把这篇文章放在了第一章的决心。主要的目标是希望借助这样一个形式,让大家能够比较清晰的认识到类似NoSQL,SchemaFree,RDBMS,CAP,BASE等等概念的本源,并了解到他们面对的主要场景,从而避免乱花渐入迷人眼的尴尬,知其然而知其所以然。
其实,软件中所谓的对象,结构体,实体,关系等概念,都只是对现实生活中的一种抽象。因为人类太过渺小,渺小到无法真正的理解和模拟这个世界,所以不得不创造出一些概念,过滤掉具体的细节而只关心他们所需要关心的事情。 这就产生了各种各样的抽象。
而SQL和关系模型,就是针对数据之间的“关系”而进行的一种抽象。
简单来说,他将一切事物都抽象为关系,并通过集合运算的方式规定了关系之间的运算过程,也因此更为严谨。比如,描述一辆车有四个轮子和四扇玻璃,那么就可以建立三张表格,一张存车的属性,一张存玻璃的属性,一张存轮子的属性,并且在轮子和玻璃的表格中,冗余车的唯一标示。这样就可以完成关系描述。如果要读取车A.id = 5车子的左前方轮子的出厂号码标示,做法一般是:查询轮子表,找到车子id=5的并且标有左前方属性的那行数据,读取他的出厂号码。
了解了关系模型,我们再来看看在关系模型产生之前,大家经常使用的层次模型吧。
层次模型其实也是非常简单的一类描述 ,还以车为例, 一辆车有唯一的标示(可以是个id,也可以只是个入口引用),然后车节点有两个子节点,一个是轮子集合节点,一个是玻璃集合节点,然后,轮子集合节点有四个节点,分别表示四个轮子,而玻璃集合有四个节点,分别标示四扇玻璃。如果要读取车A.id = 5车子的左前方轮子的出厂号码标示,做法一般是:找到顶节点车A,然后查找该节点的子节点,轮子集合节点,然后遍历4个子节点,找到标有左前方属性的车轮,读取其出厂号码。
从上面简单的例子对比来看,相信大家立刻就能看出关系模型与传统的层次or表格模型的最大差别。也就是 用户不再需要关注从车->轮子集合->轮子本身,这个存取路径, 只需要关注于核心的查询逻辑(车子id=5 , 车轮属性是左前方),就可以立刻找到数据了。 使用关系模型,因为模型相对的比较简单,并且数学证明比较严密,所以很快被大家接受。
因此在市面上已经很少出现层次模型or网状模型了。
在互联网时代之前,数据库的研究领域更多的集中在关系模型与前端业务开发模型不匹配这个问题上, 众所周知的, 在面向对象的语言产生之后,继承,多态,充血模型已经成为了程序语言的标配,我们在这里不去讨论是面向对象好,还是函数式编程好这样没结论的问题,只来简单的浏览一下面向对象与关系模型的阻抗失配问题即可。
如果大家写过业务逻辑,一定也会觉得把数据库里的数据转变为程序对象是一件蛋疼无比的事情吧。将面向对象里面的继承和组合这类概念硬套到关系数据库上,需要耗费比较大的精力才能完成。
为了解决这些问题,一种思路是在程序层做这个ORMapping的转换,这类工具主要是hibernate、ibatis等工具。另外一个思路是在数据库层面做这件事,比如oracle一直宣传自己是ORDBMS。甚至甚至,连脚本语言框架比如ROR , django的核心目标之一也就是解决这个阻抗失配的问题~
因为类似java/c++/.net这样的语言是静态编译的,所以就必须要求用户要在代码中明确的定义对象的属性名字和类型,而在数据库内,也有一套对应的列名和数据库类型信息。 一张表有50多个字段,每次字段变更,都必须保证用户代码内的对象内的属性和数据库中的数据准确对应。这非常消耗时间,也非常容易错。
为了解决这个问题,要么是从程序代码生成关系模型,要么是从关系模型反向生成程序代码。 这两种方式都会面临程序逻辑与关系模型不匹配的问题,于是写ORMapping就成了一件蛋疼无比但又不得不做的事情。
为了自动化,有大量的工具组件出现在这里,比如hibernate,比如ibatis,他们主要作用就是将我们的对象模型转换为关系模型,不过这类工具最大的问题就是,学习工具本身的成本很高,甚至高于自己去做对象关系映射本身,而且经常会因为对ORMapping掌握的不够精深,造成很多低效的查询,拖慢了整体性能的问题。
还有一些人为了偷懒,放弃使用对象bean来表示数据库中数据。他们一般会采用Map映射来表示数据库中一行数据,使用这种方式,Map的key就是列的名字,value就是列的值,如果要表示多行数据,那么就是一个List<Map>的结构。使用这种结构,程序就可以自动的根据数据库给出的列名原信息来自动生成Map结构。但这种方式的问题是,丢失了面向对象所带来的良好的封装特性,经过多层传递与处理后,用户很难辨识哪些是数据中间过程数据,哪些是数据库原始数据。数据Map对象会膨胀的非常厉害,以至于无法管理。
脚本语言的核心目标之一也就是解决这个阻抗失配的问题, 脚本语言因为是动态编译的,所以动态对一个对象增加或减少属性变得非常简单而清晰,所以对象内的数据可以直接根据数据库内的数据进行内省获得,不在需要人工维护,同时又不会出现因为Map结构所导致的代码结构不清晰的问题,所以ROR这类的工具可以直接进行对象关系映射,极大地提升了小业务系统的生产力。
可惜,对象数据库和xml数据库,都没有形成一统天下的新浪潮,一直不瘟不火的缓慢发展着。
随着互联网的爆发式发展,数据库概念领域又一次发生了摇摆,伴随着互联网的特殊需求,一批有着新鲜血液的NoSQL数据库涌现了出来,层次模型又从封印中苏醒,站在了大家面前。
这里就自然而然会有一系列的疑问产生了出来,为什么层次模型变种的NoSQL会出现并得到了一些人的认同?他满足了什么需求? 关系模型在什么地方不能满足大家的需求了?
那么,我们就从应用场景出发,尝试回答一下这些问题吧。