关系模型和对象模型的究竟匹配还是不匹配?

2019-04-14 19:21发布

在过去的很多年,我以为关系模型就是传统的企业应用当中DBA设计的那些无数冗余字段,多个模型合并到一个表里面的数据库设计方式,这种数据库设计非常适合复杂的OLAP类型的查询,他可以有效的消除多表联合查询,而我们大家都知道,大表的复杂关联查询是性能杀手,一旦无法有效利用索引,导致了全表扫描,等待你的只有数据库服务器硬盘灯的狂闪不止,和无数进程阻塞在IO WAIT状态的无奈。

我前几个月订购了一本人邮图灵出版的《MySQL 5 权威指南》第三版中文版,买这本书只是因为有人送我China-Pub的优惠券,我就顺手买本MySQL的书,用来管理JavaEye服务器的时候备查的。其实这本书内容很一般,他说的东西我都知道了,所以这本书我拿过来随手翻了翻就感觉到买的不值得。但是当我随手翻到第8章第5节第138页介绍什么是三大范式的时候,我终于知道我错了。

从138页到142页,作者深入浅出举例说明了三大范式,我被震了,就这几页让我觉得买这本书值了。对于我这个不是计算机科班出身的人来说,到现在才知道什么是三大范式不算可耻。我震惊的只是三大范式和我们现在遵循ORM的原则去设计数据库的方式如出一辙!我简单摘要书中内容如下:

[quote]第一范式:
1、内容相似的数据列必须消除(消除的办法就是再创建一个数据表来存放他们,建立关联关系)
2、必须为每一组相关数据分别创建一个表
3、每条数据记录必须用一个主键来标示

第二范式:
1、只要数据列里面的内容出现重复,就意味着应该把表拆分为多个表
2、拆分形成的表必须用外键关联起来。

第三范式:
1、与主键没有直接关系的数据列必须消除(消除的办法就是再创建一个表来存放他们)[/quote]

这三大范式就像给ORM的人如何设计数据库写的指南:
[quote]第一范式:
1、每个持久对象映射一张表
2、每个持久对象必须有一个主键

第二范式:
1、持久对象要有内聚性,冗余的内容拿出去,单独创建持久对象
2、持久对象之间的关系用外键关联

第三范式:
1、持久对象要有内聚性,无关的内容拿出去,单独创建持久对象[/quote]

关系模型和对象模型是不是在存储概念上一致,就不用多说废话了。

说关系模型和对象模型“阻抗不匹配”,当然是有不匹配的地方,比方说对象模型当中特有的“继承”,“组合”,“聚合”,“依赖”的概念在关系模型当中是不存在的,但是这种模型的“阻抗不匹配”最终在存储模型是还是能够统一起来的,这就是ORM的作用:

1、对象的继承关系可以表达为三种不同的关系存储模型:整个继承数一张表;每个继承层次一张表;每个对象一张表

2、对象的组合和聚合可以用主外键关联的表来存储,它可以表达1:n,n:1和n:m的关系

3、对象的依赖关系和存储无关,所以不需要ORM做什么。

所以结论就是这样:

[color=red][b]关系模型和对象模型存在概念上的阻抗不匹配,但是在关系数据库的存储模型上是一致的,无论你从关系模型的三大范式理论出发,还是从对象模型的ORM理论出发,最终一定会得到一致的数据库表设计。[/b][/color]

这里值得我们反思的一个问题是:为什么传统的数据库应用人们这样漠视和违反三大范式?在很多所谓的金融、电信等超级大项目当中,连主键都没有的表比比皆是,一张表上百个字段,字段之间没有什么逻辑关系的情况比比皆是?

我想答案在于:传统的数据库应用软件开发,程序员很难从符合三大范式的数据模型当中获得有效的查询性能。符合三大范式就意味着数据库表会拆分的很细,表间关联很多,统计分析查询就不可避免的导致n张表的联合查询,在没有有效的应用层缓存的情况下,这种查询无可避免的性能低下。这使得程序员宁肯违背三大范式,而选择查询性能优先的数据库设计。

但是我们现在不一样了,有了良好的ORM框架和应用层的对象缓存机制,我们可以做到:让比较简单的查询根本不打扰数据库,让比较复杂的查询尽量少的扫描表记录,其最终达到的效果在OLTP类型的应用上面效果远远超过传统的方式。

以JavaEye网站为例:JavaEye使用了Rails的ActiveRecord ORM,表设计符合三大范式,所有页面都是动态页面,要对数据库发送大量查询,很多Web页面至少要向数据库发送50条以上的SQL语句。根据对数据库和Memcached Server的统计数据表明:JavaEye网站平均每秒向数据库发送140条SQL语句,平均每秒向Memcached Server发送250次缓存查询,缓存命中率大概为85%,也就是说缓存服务器要比数据库服务器繁忙将近一倍,而Ruby应用程序的数据有60%是来自Memcached Server,而只有40%是直接来自MySQL的。

为了加深大家印象,再给大家一个数据,目前JavaEye的Web服务器CPU负载在40-60%左右,而JavaEye的数据库服务器CPU负载只有20%-30%,IO WAIT几乎没有。所以良好的遵循三大范式,利用好ORM和对象缓存,可以取得非常棒的应用性能,还可以让你的数据库更加轻松。
最后,我的结论就是对象模型和关系模型在数据库存储上不存在阻抗不匹配,面向对象的程序设计和面向数据库的程序设计应该是一致的,而不应该是对立和冲突的,请不要把面向对象和面向数据库对立起来,不是他们对立,而是你不了解什么才是真正良好的设计。