论坛首页 Java企业应用论坛

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

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

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

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

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

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

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


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

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

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


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

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

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

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

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

所以结论就是这样:

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

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

我想答案在于:传统的数据库应用软件开发,程序员很难从符合三大范式的数据模型当中获得有效的查询性能。符合三大范式就意味着数据库表会拆分的很细,表间关联很多,统计分析查询就不可避免的导致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和对象缓存,可以取得非常棒的应用性能,还可以让你的数据库更加轻松。
最后,我的结论就是对象模型和关系模型在数据库存储上不存在阻抗不匹配,面向对象的程序设计和面向数据库的程序设计应该是一致的,而不应该是对立和冲突的,请不要把面向对象和面向数据库对立起来,不是他们对立,而是你不了解什么才是真正良好的设计。
   发表时间:2007-12-27  
引用
传统的数据库应用软件开发,程序员很难从符合三大范式的数据模型当中获得有效的查询性能。符合三大范式就意味着数据库表会拆分的很细,表间关联很多,统计分析查询就不可避免的导致n张表的联合查询,在没有有效的应用层缓存的情况下,这种查询无可避免的性能低下。这使得程序员宁肯违背三大范式,而选择查询性能优先的数据库设计。


这是关键点
0 请登录后投票
   发表时间:2007-12-27  
引用

对于我这个不是计算机科班出身的人来说,到现在才知道什么是三大范式不算可耻。


好像理工专业都要学习数据库吧,看来robbin大哥那节课是逃课了,呵呵。
0 请登录后投票
   发表时间:2007-12-27  
确实,任何对象,通过key层层关联,确实最终都可以转换为二维关系。存储模型是可以匹配的,但是对象设计和关系设计在设计角度上确存在不可逾越的阻抗

但是,为什么最终的效果,确是数据库模型和对象模型之间如此大的差异?

当然,性能是一个原因,但是低范式中并没有不允许字段的冗余,个人觉得它不是根本原因。

很多时候不是表比对象多几个字段,或者多几张表而已,甚至是,完全表达的就不是一个概念,关键差别在于抽象层次不同

有人可能会说,数据库表也可以抽象阿,完全可以设计一张通用表,抽象表达多个概念。

确实,没错。

可是,存储的抽象有多大意义呢?

就拿电信模型说事,在电信BOSS模型中有一个概念‘销售品’。商品,商品包,增值业务,营销资源都可以是销售品,对应到表设计,演变成为商品表,商品包表,增值业务表,营销资源表。那么,销售品表需不需要存在呢?有多大意义?

但是!演变到对象设计,销售品对象代表着重大意义,它是对商品,商品包,增值业务,营销资源等对象共用业务规则的抽象,承载着大量的行为和约束。

所以,实际情况,对象模型要映射到存储模型总是不那么自然。

不过,可悲的是,电信领域,几乎不存在这种阻抗,在核心系统领域,我几乎没见过哪个系统是用较好的面向对象思想进行的设计,大家都是表设计驱动,所谓的电信规范就是一个标准的存储模型标准。

金融不好说,接触的少。
0 请登录后投票
   发表时间:2007-12-27  
我想深入的探求一个问题,数据库的设计范式是为了什么?它可以带来什么好处?我看到一篇讨论的文章:

http://www.cnblogs.com/tongtkk/archive/2005/04/30/148187.html

引用
第一范式:

对于表中的每一行,必须且仅仅有唯一的行值.在一行中的每一列仅有唯一的值并且具有原子性.

(第一范式是通过把重复的组放到每个独立的表中,把这些表通过一对多关联联系起来这种方式来消除重复组的。)

第二范式:

第二范式要求非主键列是主键的子集,非主键列活动必须完全依赖整个主键。主键必须有唯一性的元素,一个主键可以由一个或更多的组成唯一值的列组成。一旦创建,主键无法改变,外键关联一个表的主键。主外键关联意味着一对多的关系.

(第二范式处理冗余数据的删除问题。当某张表中的信息依赖于该表中其它的不是主键部分的列的时候,通常会违反第二范式。)

第三范式:

第三范式要求非主键列互不依赖.

(第三范式规则查找以消除没有直接依赖于第一范式和第二范式形成的表的主键的属性。我们为没有与表的主键关联的所有信息建立了一张新表。每张新表保存了来自源表的信息和它们所依赖的主键。)

第四范式:

第四范式禁止主键列和非主键列一对多关系不受约束


第五范式:

第五范式将表分割成尽可能小的块,为了排除在表中所有的冗余.


引用
范式目标之一:逻辑正确。例如,经理管理部门信息,人事管理员工。如果采用范式分成“部门”“员工”主子2表,人事管理员工时,只能为员工指定现在存在的合法部门ID。如果不采用范式,部门和员工的信息在一个表中,管理员工时,就可能因为人事疏忽、或程序不完善为员工指定了一个错误、不存在的部门名称。或者同一个部门,在不同的记录中,简称一样,名称却不一样等等。这样,公司的部门就被搞乱套了。范式化的数据模型具有健壮性,能够抵御一定程度的人为和程序的疏忽,保证数据的完整性。

在实际业务逻辑中,会遇到前面几位提到的例子,是否需要保存冗余的历史信息,也就是范式中最关键的词汇“依赖”是否在发生变动时永远都能够成立。否则,就不是“依赖”,不用范式。就这个送货地址变更例子而言,怎样看待这个“依赖”成立,可以站在不同的角度上,短时间段内,还是系统的全寿命内,得出的结论自然不同,每个人的不同观点在自己的角度上看都是对的,但是最终还是要看业务规则是否要这个“依赖”。

范式目标之二:成本、代价、"cost"。当初制定范式时的代价和现在的代价含义已经大不相同。那时存储是稀缺资源,需要各种手段节约存储(Y2K问题就是一个佐证)。但是现在,存储是极廉价的(无论大机还是微机,扩内存和硬盘的代价远低于升级CPU或升主频),而时间和程序员是稀缺资源。采用范式最大的好处是节约存储,但坏处是做某些复杂查询时,需要高级的程序员写出极复杂的多级关联查询语句。我曾经为一个范式系统写过一条select查询语句,仅一句(含多次关联、集合等操作)就有近2000字长,如果在DOS下整个一屏幕都显示不下,天哪!这种典型的范式系统浪费了最稀缺的资源:技术员、开发时间、运行时的等候时间,而且这样的程序的维护性几乎是0。

另外一个考虑因素是后来引出来的。原来的系统多是OLTP,面向交易处理,插入、删除、修改操作占多。有实践工作经验的人都知道,在这样的范式系统中,要做灵活复杂的报表有多么痛苦,就算是有各种智能辅助报表工具也是令人遗憾。而现在的系统,决策、分析占了很重要的角色,如果要问数据库仓库的分析工具为什么能够快速做出各种复杂的分析?关键就是非范式化。但是我们设计的每个系统都能够使用OLTP加一个数据库仓库这种配置吗?显然不现实,在系统中实现一定的非范式化,可以简化查询、报表的工作,丰富其功能。

非范式系统的最大的问题是数据的一致性,DBMS的KEY & FKEY帮不上忙了,就需要额外的机制来保证。怎样权衡,还需要实践,就不是一次能够讲清楚的了。
0 请登录后投票
   发表时间:2007-12-27  
是不是我们可以这样说:

数据库三大范式,乃至于五大范式目标在于:

一、保证数据库的数据一致性
非范式化会导致数据大量冗余和无序

二、数据库维护的灵活性
修改记录不会出现需要多处修改的问题

三、节约存储
早期存储设备非常昂贵,当然现在这个目的显得不必要了。

范式带来的问题:

一、OLAP类型的查询性能低下
过多的表关联带来的查询性能问题

看来范式和ORM还真是一对阿,连优点和缺点都一样的。
0 请登录后投票
   发表时间:2007-12-28  
多表复杂关联导致的瓶颈问题,可以使用物化视图来解决;通过使用空间来换速度好于改变设计换速度
0 请登录后投票
   发表时间:2007-12-28  
leadyu 写道


金融不好说,接触的少。


现在做银行系统,似乎也没有哪个用面向对象的设计方法,基本都是数据表驱动的开发……
0 请登录后投票
   发表时间:2007-12-28  
andyao 写道
引用
传统的数据库应用软件开发,程序员很难从符合三大范式的数据模型当中获得有效的查询性能。符合三大范式就意味着数据库表会拆分的很细,表间关联很多,统计分析查询就不可避免的导致n张表的联合查询,在没有有效的应用层缓存的情况下,这种查询无可避免的性能低下。这使得程序员宁肯违背三大范式,而选择查询性能优先的数据库设计。


这是关键点


因为数据库设计遵循先范化,再优化。所以基本上最后的设计都不是范化的,也很难找到这样的范化设计。
特别是在一些为了统计分析方便的系统中,增加了大量冗余的统计信息,此特征更为明显。不必如此惊诧。


0 请登录后投票
   发表时间:2007-12-28  
robbin 写道

范式带来的问题:

一、OLAP类型的查询性能低下
过多的表关联带来的查询性能问题


在最有效使用连接方式的前提下,多表关联是否还会导致低下的查询性能。一个符合完全3范式设计的数据库在查询中对于最有效连接方的使用是否可与充满冗余字段的数据库设计相比,感觉问题的关键在于磁盘IO,如果全表扫描,大家都一样;有足够内存,索引可以在内存命中;而计算连接又依赖于索引,如果连接的计算过程可以在内存完成,感觉连接不应该带来太多的性能差异,担心的倒是数据库引擎和人是否有足够智能控制查询的执行。
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics