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

来源:互联网 发布:碳纤维 知乎 编辑:程序博客网 时间:2024/06/01 09:51

在过去的很多年,我以为关系模型就是传统的企业应用当中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和对象缓存,可以取得非常棒的应用性能,还可以让你的数据库更加轻松。
最后,我的结论就是对象模型和关系模型在数据库存储上不存在阻抗不匹配,面向对象的程序设计和面向数据库的程序设计应该是一致的,而不应该是对立和冲突的,请不要把面向对象和面向数据库对立起来,不是他们对立,而是你不了解什么才是真正良好的设计。

 

-----------------------------------------------------------------------------------------------

-----------------------------------------------------------------------------------------------

评论

31 楼 ileile 2008-07-25   引用
不得不说,你确实太菜了...

这不是科班不科班的问题,而是对需要了解的知识缺乏敏感性....
30 楼 firmgoal 2008-04-13   引用
sslaowan 写道
我觉得对于软件专业科班出身的人而言,此帖应该属于入门帖吧,尤其是实体关系建模与对象建模的历史渊源,很多软件工程的书都会提及,并且Robbin号称精通Hibernate,怎么会到2007年末才接触到三大范式这种在关系数据库理论中最为基础的知识呢?


懂实战的不一定知道他平时掌握的原则用书面的东西怎么表达,或者说,他突然有一天会发现,原来他们一直说的那些东西,就是所谓的这些东西...

29 楼 firmgoal 2008-04-13   引用
diogin 写道

只要索引设置得好,性能根本不是问题:

mysql> EXPLAIN SELECT * FROM user INNER JOIN note ON user.user_id = note.user_id WHERE user.user_id = 1234567;
2 rows in set (0.00 sec)

mysql> EXPLAIN SELECT * FROM note INNER JOIN user ON note.user_id = user.user_id WHERE note.user_id = 1234567;
2 rows in set (0.00 sec)

mysql>

所以不要武断地认为大表的关联是性能杀手。


用例太简单了,where 条件用的是唯一索引,这样关联下来肯定快。我说一个复杂一点但是很普遍的用例,你title like一个'%xxxx%',再order by title, 然后分一个页,试试。
28 楼 firmgoal 2008-04-13   引用
在做项目的过程中,我找到了一点不匹配的地方,那就是对象是数据与操作的集合,对象可以只有操作而没有数据,而数据表是数据的集合,只有在有数据的情况下才有存在的意义。
面向对象的目标应该是为了避免数据和操作的冗余,而数据库范式的目标仅仅是避免数据的冗余。
比如:Resource <- SpiritResource, 
     Resource <- EntityResource,
     SpiritResource <- User, Organization, OrganizationalUnit...
     EntityResource <- WebSite, Column...

SpiritResource和EntityResource是抽象类,且不含有任何属性,它们的存在是为了避免一些代码的冗余,这样,它们无法和数据表对应起来,因为,只有一个id列的表没有什么存在的意义。
27 楼 sslaowan 2008-04-12   引用
我觉得对于软件专业科班出身的人而言,此帖应该属于入门帖吧,尤其是实体关系建模与对象建模的历史渊源,很多软件工程的书都会提及,并且Robbin号称精通Hibernate,怎么会到2007年末才接触到三大范式这种在关系数据库理论中最为基础的知识呢?
26 楼 diogin 2008-01-27   引用
CREATE TABLE user (
  user_id int(11) NOT NULL AUTO_INCREMENT,
  name varchar(16) NOT NULL,
  pass char(40) NOT NULL,
  PRIMARY KEY (user_id)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

CREATE TABLE note (
  note_id int(11) NOT NULL AUTO_INCREMENT,
  user_id int(11) NOT NULL,
  title varchar(16) NOT NULL,
  content text NOT NULL,
  PRIMARY KEY (note_id),
  KEY user_id (user_id)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

以上两各表各1000万条记录:

mysql> SELECT COUNT(*) FROM user;
+----------+
| COUNT(*) |
+----------+
| 10000000 |
+----------+
1 row in set (0.00 sec)

mysql> SELECT COUNT(*) FROM note;
+----------+
| COUNT(*) |
+----------+
| 10000000 |
+----------+
1 row in set (0.00 sec)

mysql>

只要索引设置得好,性能根本不是问题:

mysql> EXPLAIN SELECT * FROM user INNER JOIN note ON user.user_id = note.user_id WHERE user.user_id = 1234567;
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
| id | select_type | table | type  | possible_keys | key     | key_len | ref   | rows | Extra |
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
|  1 | SIMPLE      | user  | const | PRIMARY       | PRIMARY | 4       | const |    1 |       |
|  1 | SIMPLE      | note  | ref   | user_id       | user_id | 4       | const |    1 |       |
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
2 rows in set (0.00 sec)

mysql> EXPLAIN SELECT * FROM note INNER JOIN user ON note.user_id = user.user_id WHERE note.user_id = 1234567;
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
| id | select_type | table | type  | possible_keys | key     | key_len | ref   | rows | Extra |
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
|  1 | SIMPLE      | user  | const | PRIMARY       | PRIMARY | 4       | const |    1 |       |
|  1 | SIMPLE      | note  | ref   | user_id       | user_id | 4       | const |    1 |       |
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
2 rows in set (0.00 sec)

mysql>

所以不要武断地认为大表的关联是性能杀手。
25 楼 Trustno1 2008-01-23   引用
http://www.javaeye.com/topic/25649
一年前的老文.
赫赫
24 楼 lgx522 2008-01-21   引用
本人以前自学数据库的时候对三大范式印象太深,以至于工作中大部分是以DB建模。而遵循范式设计出来的模型基本上是符合OO的。

实践中,以此模型为基础的系统性能是相当好的(大家要考虑到外键索引、尤其是数字型索引所带来的巨大优势),根本不是某些所谓DBA想像中的过不了日子。相反,那些个高冗余度的系统,才是难以维护、一动百摇。
23 楼 drinkjava 2008-01-20   引用
   对象还是关系? 没有绝对的好坏,这要从具体的设计方、需求方和任务本身谈起。
   我以前是学机械的,就拿机械作比方吧,对象模型就象比三维立体建模,是一个有机的整体,关系模型就象是平面三视图,是对这个整体从各个视角或者截面进行的描述。三维建模是没有冗余的,平面三视图理论上是可以做到没有冗余,但事实上很少有人能画出没有冗余信息的三视图。通常,平面图是唯一用来进行沟通的标准工程语言,但也有个别例外是,有些高级机床可以直接读取三维模型的进行加工的。
   有一天,一个工厂对你说,我需要这么这么一个机器, 给我设计一下吧....
   于是,我们就来分析:
   从设计者角度:
   如果我自已是个老工程师,不会SolidWork,Pro-E之类高级三维工具,只会图二维图,哪毫无疑问,只能给出平面图了。 (只懂关系模型)
   如果我SolidWork之类工具用的很熟,很讨厌一张一张的图二维图,哪肯定是先三维建模, 然后再由三维建模导出平面图。(只懂对象建模)
   从工厂的角度:
   如果这个工厂全是些高级数控中心,只接收三维数据,哪没办法,只能要求设计者采用立体建模了。 (甲方能理解你的对象模型,需求变更时能直接在这个模型层次上展开讨论)
   如果工厂里全是些老机床,只会看平面图的工人,即可以直接给平面图,也可以三维建模后再转成平面图(甲方只理解关系模型,关系模型是双方交流的语言)
   从任务本身:
   如果机器是由常见的铸件螺栓螺母标准件组成,用立体建模肯定是最快了.(对象模型容易归纳出)
   如果要设计的不是机器,而是一件新款的裙子,这时只会用solidwork的头疼了,因为solidwork很难画出个三维的裙子。 (业务本身不适用或没有必要对象建模)
   总之要考虑的因素太多了,以上都是极端的情况,还有介于中间的,总之具体的情况具体对待,空泛的谈谁好谈坏是没有意义的。
22 楼 canonical 2008-01-06   引用
    关系数据库模型在理论上主要解决的是消除数据冗余的问题。关系模型的数学基础是所谓的集合论,而集合的基本含义正是一组具有某种原子性的互不相同的元素。面向对象技术是对相关性进行局域化的一种手段(相关的数据和操作聚集到同一对象名义下),在这一局域化过程中,相同的元素被识别出来,成为独立的对象。从某种意义上说,关系模型与对象模型是殊途同归的过程,是从不同侧面对同一事物的反映。关系模型中,我们关注的重点是元素组成的集合,允许的连接关系定义在集合之上。而在对象模型中,我们关注的首先是横向关联的实体,实体之间具有稳定的联系。在概念层面上,从对象模型映射到一种关系存储模型只是一个分组问题。为了断开实体之间的直接联系,关系模型创造了一个id字段,而对象模型并不是需要显式id的。在关系模型中,关联并不是通过某种存在的结构来表达的(一个实体持有另一个实体的指针,拥有直接联系),而是将直接关联问题弱化为某种计算过程,我们必须检查id的值(不是某种直接的存在性),通过某种运算过程才能重新发现数据之间的关联。
   
    通过id(伴随一个匹配计算过程)来进行间接关联对于保证模型的一致性是非常关键的。在ORM中恢复了对象的强关联其实会造成很多潜在的复杂性。例如为了维护对象层面结构的一致性,在更新父子关系的时候,我们需要同时调用 child.setParent(parent); parent.getChildren().remove(child); 当关联结构更加复杂的时候,这里所需要的维护工作是随之增加的。不过,在ORM中,对象的形态是暂时性的。在ORM的一次session的操作过程中,对于对象状态的修改可以是不一致的。例如我们可以只调用child.setParent(parent); 而不需要同时  parent.getChilren().remove(child); 只要我们在此次session操作中,不需要同时用到parent.getChildren(). 这种关联的暂时性对于很多ORM应用来说是必不可少的。
   
    对象模型中可以直接表达的结构关系比关系模型要丰富一些,例如继承关系,many-to-many, one-to-list等。但是所有这些都不是真正本质性的差异。抛弃概念诠释,基类与众多派生类之间的关系基本上可以等价于一组one-to-one关系。而当关联对象本身的重要性凸现出来的时候,当我们无法把它约化为对象上的一些附属特性的时候(例如数组的下标),我们必然要建立相应的关联对象,而这正对应于关系模型中的中间关联表。中间关联表上增加额外的字段是一个自然的扩展过程,而对象模型上做这样的扩充往往表现为形态上的重大的不兼容的变化,例如从getManyToManyEntity() -> getToManyRelation(), 这实际上意味着这里的对象形式是偶然的,简化的。
   
    在原始的关系数据库模型中,所有的表之间的地位是平等的,所有字段之间的地位是平等的(主键和外键在参与数据关联时和其他字段的处理方式一致)。这种概念上的均一性和普遍性往往被认为是理论的优美之处。但是现实世界是复杂的,发展的方向就是逐步识别出不同之处,并找出自然的表达形式将这些不同表达出来。均匀的关系模型是对称性最高的,最简化的模型。在面对物理约束时,它隐含的假设是集合之间很少发生相互作用,单表(表单到数据表之间的映射)和主从表是最广泛的情况。试着想象一下关系模型,在思维中一般我们只能看到两个数据表,当考虑到多个表的时候,因为这些表之间没有明确的可区分性,因此它们的意象是模糊的。只有明确意识到主键,外键,主表,从表,字典表,事实表,纬度表这些不同的概念的时候,当对称性出现破缺的时候,我们思维中的模型才能够丰富化起来。
   
    关系模型理论应用到数据库具体应用中时,并不需要死守关系范式教条,它们只是描述了某种极端化的对唯一性的追求。面对具体应用的时候,理论本身也在不断丰富化。我并不把现实应用中必然需要增加冗余字段看作是关系理论失效的结果。从关系完全分解,到关系完全不分解之间,我们可以建立大量的模型。建立冗余字段的时候,我们存在着大量可能的选择,到底哪一种选择是最优的,理论方面仍然可以给我们以具体的指导。理论在各种不同纯化程度的关系模型中都可以给我们以直观的建议。数据仓库理论中建立的snowflake模式和star模式,强调了针对主题域的允许部分冗余的关系分解。这里实际上是强调了表之间的不同性。不再是所有的表都处于同一地位。Fact Table和Dimension Table之间的区别被识别出来,并被明确处理。在我看来,这是原始关系模型的一种自然发展,它也是关系模型理论的一部分。理论不应该是单一的,而是提供一个模型级列,在不同的复杂性层次上,我们可以根据理论的指导选择具体的实现模型。
   
    关于ORM http://canonical.javaeye.com/blog/111500
    关系模型中的所谓关系是在使用时刻才定义的,所有建立关系的方式在某种程度上都是等价的,也是外在的。而在ORM中主键与外键之间的关联被独立出来,成为模型内置的部分。这在很多时候简化了数据查询的结构构造过程。
    在ORM中主键因为缓存的存在而显出与其他字段的区别。ORM的使用使得数据存储的分解策略得到扩充。并不是所有的表的更新频度都是一致的,而且表中的数据量大小也不同。字典表一般较小,而且很少更新,可以安全的复制。在整个数据存储框架中,ORM作为独立的技术元素参与数据存储过程,通过主键提供缓存服务,产生了新的数据分布模型,提供了新的性能优化契机。
21 楼 lzmhehe 2008-01-04   引用
javaeye是个网站,大家关注的地方的有很多重叠的地方,
例如首页,每个版块的第一页内容,再加上同时在人多
使用orm缓存效果很明显,
但是有些情况,本身同时在线的人数就很少,再加上人人关注的地方都不同,缓存效果肯定不明显,要求在几秒内响应,单靠符合3范式orm缓存,我觉得不现实

20 楼 commanderhyk 2008-01-03   引用
看看这样的需求,三五百万行的表有三四张,一对多,多对多的关系都有,列多的有七八十,少的也有十来列,所有表的80%字段都要查(还有好多张十来万的小子表,暂时先忽略掉不讨论).而且是关联查询.有没有人搞过这类的东西?性能不好可把我逼疯了.现在的查询选项的表单只是录入框就有一两百个,查询哪个表客户自己选.(我靠,都想培训客户去写sql了,呵呵)目前做了一个sql语句的生成器,根据表单的命名自动产生查询条件和表关联.功能倒是实现了,发现性能有问题.物化视图稍微能缓解一下,刷新恐怕有点问题.指标不治本.
我现在考虑适当的冗余,减少联接,也可能没有使用查询缓存的缘故.但是由于项目是在别人开发过的旧系统升级新功能,所以只敢用jdbc.这就是目前的情况.不知道像这样的系统,这种需求使用orm+Hql能行吗?如果开启缓存功能.数据有可能是另外的系统更新.我的缓存岂不都废了.先不考虑更新问题.即便开了查询缓存性能又能提高多少?
劳驾各位给看看,有没有好办法?指点一二.谢谢
19 楼 dualface 2008-01-03   引用
我认为如果把 ActiveRecord 和数据库设计放在一起讨论,得出的结果肯定是对象和数据表的设计几乎一致。

原因在于 ActiveRecord 本质上就是以数据库记录为出发点来设计和实现的。ActiveRecord 是将一个数据库记录封装为对象,并且提供一个实现领域逻辑的载体。这样一来,一个 ActiveRecord 对象必然和数据库结构一一对应。

如果一个领域对象无法用简单的数据库结构来表现,那么就没有办法用 ActiveRecord 来实现这个领域对象。

可为什么许多开发者感觉用 ActiveRecord 确实很方便呢?
那是因为他们开发的都是典型的“CRUD“操作应用程序。

比如 JavaEye 这个网站。不会有复杂的业务逻辑,所以也就没有了复杂的对象体系。几乎所有的一切都可以用 ActiveRecord 对应一个数据表来解决。

但如果是 leadyu 提到的应用,复杂的对象体系是必然的设计结果。而且面向接口的架构,又如何能够简单的映射到数据库结构呢?
就像提出 ActiveRecord 模式的 MF 本人所说,一旦一个对象不能找到对应的数据表设计(原文记不清楚了),那么 ActiveRecord 模式就不适合了。

所以楼主所说的“三大范式和我们现在遵循ORM的原则去设计数据库的方式如出一辙”不完全正确。
我想更准确的说法是:“三大范式和我们现在遵循 ActiveRecord 的原则去设计数据库的方式如出一辙”。

最后,我觉得 ActiveRecord 不能和 ORM 划等号。因为 ActiveRecord 的前提就是对象可以找到对应的数据存储结构。这和 ORM 原本打算解决的对象和关系式数据库不匹配问题有巨大的区别。

理想的 ORM 应该不需要对象设计去“迁就”数据库结构。而这一点,ActiveRecord 是做不到的,它要求必须将对象和数据库结构匹配起来。
18 楼 yyjn12 2008-01-02   引用
  我怎么记得萨师煊 王珊编写的数据库系统概论,做为我所读的大学的本科计算机专业课教材,其中对范式的描述似乎和robbin所说不太一样哦?

好象说是第一范式(1NF)已经由目前主流的数据库给做了,不符合第一范式,那么根本就不会是关系数据库.也就是说,每个列都是不可再分的数据项.表是二维的.

  理论课没学好呀,实际工作中用冗余字段也用过,觉得换来效率上的提升,在某些情况下还是值得的呀,而又与所学理论课程相矛盾,着实曾经迷茫过一阵子,即使到了现在,由于技术不扎实,也没有个清晰的认识.
17 楼 mingj 2008-01-02   引用
Godlikeme 写道
andyao 写道
引用
传统的数据库应用软件开发,程序员很难从符合三大范式的数据模型当中获得有效的查询性能。符合三大范式就意味着数据库表会拆分的很细,表间关联很多,统计分析查询就不可避免的导致n张表的联合查询,在没有有效的应用层缓存的情况下,这种查询无可避免的性能低下。这使得程序员宁肯违背三大范式,而选择查询性能优先的数据库设计。


这是关键点


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





数据库设计就概念设计而言,就是一个面向对象设计的过程
通过绘制E-R图,拆分关联和属性,逐步范化,基本上就是对象模型的设计过程

个人认为:
大部分“巨表”都是数据库设计没有学好或者图方便而添加的产物
少量的巨表是在条件不允许的情况下不得已的产物
16 楼 tobato 2008-01-02   引用
diogin 写道
领域模型设计不应该掺合数据表设计的做法我觉得是自己给自己找麻烦,除了这一点,其它的我都认同领域驱动设计。


没看明白,到底是要在领域模型设计的时候考虑数据库表设计还是不考虑。

我觉得数据库表的设计在领域模型设计的时候是不应该考虑的,但是接触过的很多人都很难一下子转变这个思路,结果是设计出的"领域模型"有很多[XX信息]命名的东西。

电信、电力、金融等大型系统出现很多数据库表结构不合理(报表统计涉及表的除外),是由程序设计开发阶段的管理、技术、人员能力等诸多因素造就的.
15 楼 dada 2008-01-01   引用
name 写道

在很多所谓的金融、电信等超级大项目当中,连主键都没有的表比比皆是,一张表上百个字段,字段之间没有什么逻辑关系的情况比比皆是

这个问题有两方面的原因:
一方面是历史遗留问题造成的,只要核心业务系统中出现这种情况,其他依赖其的周边系统必定也会出现类似的情况,到后期积重难返。
另一方面是由于项目"鸿沟"造成的,核心系统中的各个模块由不同的人负责,缺少整体的规划(数据和技术两方面)。这类问题一般都是靠大集中项目来解决,但是目前没有见过特别成功的案例。

PS:高范式和中国式报表存在严重的阻抗不匹配,如果系统中存在大量非报表工具处理的报表,降低范式貌似是最好的选择了。
14 楼 diogin 2007-12-31   引用
所谓的不匹配实际上不在数据的冗余与否,而在于继承和聚合。前者在对象领域是一个extend,后者是一个对象引用。而在数据库领域,两者都需要进行一次join。而数据库三范式确实跟对象设计有很大的共同点,在这一点上我一直把两者统一进行处理,因而所谓的领域模型设计不应该掺合数据表设计的做法我觉得是自己给自己找麻烦,除了这一点,其它的我都认同领域驱动设计。
13 楼 koda 2007-12-31   引用
根据各位2007年的发言,我这样总结和吸收:
1. 期望完全脱离RDM产品从ORM层思考和设计持久层,这也许只是个梦想,或者根本就是个幻想。
   1).任何ORM产品和RDB产品有缝隙,不可能像想象中那么平滑。
   2).斗胆猜测:大家在做项目(数据库类)时的持久层设计肯定是个综合的工作, 补丁式的:Cache, RDB产品,ORM产品样样知识不可缺。(实战内幕有待楼主另开帖)
2. ORM功不可没:
  1).ORM首先的功劳是简化开发代码,OO思考问题。
  2).ORM在性能方面比大部分人想象中做得更好(缓存)
3.三范式未必100%遵从,只要达到系统的目标。

蜷缩在窗口的小沙发上,突然想起王梵志,道是:

我昔未生时
冥冥无所知
天空强生我
生我复何为
无衣使我寒
无食使我饥
还你天公我
还我未生时

谨此纪念我浑噩的2007

12 楼 tobato 2007-12-29   引用

LZ 的结论:

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

得出一致的数据库表设计这个值得商榷,貌似得到相似的是可能的,因为领域中的基本概念是一致的。比如医院系统中的处方、患者。 从关系模型来看,对象模型来看都会有这些东西。

可是分析的方式是有差别的,也就是说设计的理念,思考的方式差别会导致我们得到最后不一样的东西!
可以做个简单的实验,找个资深的用关系模型设计的开发人员,再找个用对象设计的开发人员,绝对得出不一样的东西!

对象模型与关系模型最大的区别就是继承的概念,也是对象模型为啥比关系模型表达能力更强的根本原因。思考的方式是不一样的。不可以单从数据库最后的表结构来断言这2种思想的差异。


对象模型能否映射到关系模型?当你面对的是关系型设计的表(或者遗留系统的时候)没有ORM工具,而你又想要使用对象模型来封装业务逻辑。那你要干的就是 把对象模型映射到关系模型上。能匹配上么? 很麻烦!(我正在干这个事情)

关系模型的逻辑能映射到对象模型的数据库中么? 没有人会干这个吧。

所以结论是: 面向对象设计与关系设计思考方式的不同,会导致不同的模型结果。
             对象模型和关系模型是不一致的,中间需要ORM工具来做一个桥梁。

 

 

评论

11 楼 swachian 2007-12-29   引用
第一范式没那么玄乎,只要表的每个字段能对应成一个属性就算满足了。这点可能因为有点太基本,很多人反而不好理解。例如,一个个人资料表里面有个儿女字段,如果保存是按"张三id、李四id”;取出时自己再根据顿号分割得到两个id,那么这种情况违反第一范式,因为儿女这个字段可以再分。
至于主键,在极端情况下就是all-key,也就是全部属性合在一起构成主键。如果两行数据完全相同,那根本就算不上关系数据库了。
所以说,金融、电信类的大项目里,虽然问题很多,但基本还是符合第一范式的。因此可以算满足范式的基本要求。虽然一个字段里需要自己再根据特殊符号进行解析的事情是存在的。

范式主要还是为了解决插入和删除的异常,一般只要不是没法插入或者删除时会把共用信息也给删除,写代码的和维护的都会接受。前者在需要给出主键的值而又给不出来的时候会遇到,后者就靠冗余来补救了。至于字段很多,那估计是逐步逐步演化成那样的。用写dao的技术,形成这种局面是正常的,毕竟操作一张表即使字段和值多一些,也不一定比操作几张表来的麻烦。ms sql的update支持对联和后的表进行修改,得到很多程序员的赞许,也是因为这个道理,虽然语句长一点、字段多一点,但是一条语句就搞定了。

应该说,程序员基本遵守了第一范式,这样保证数据库可以做的下去;而弱化了主键的概念来减少自己在insert时的麻烦;delete的异常一个靠冗余,另一方面通过设置isdelete=true来解决,这样就不会真的发生删除异常;update反正更新几张表和更新多条记录比起来,在写dao的情况下,也还算凑合;至于select,冗余的查起来可能还方便。

不过有了ORM,情况就变了。agile web那本书里面并没有直接给出以数据库为主还是以object为主,但实际上由于activcerecord这个中间层存在,个人认为从哪个角度出发已经都无所谓了。而她所提供的save和分几步查询的这种方式,使得设计朝第三范式靠拢的意义变大。

10 楼 bobo 2007-12-29   引用
我觉得可以理解为规范性和性能之间的一个权衡。在当前硬件条件有限的情况下,牺牲规范来换取性能是必要的。以电信计费系统来看,基本上2000年前的大的计费系统都是硬编码,设计规范动态计费规则的软件通常都满足不了月末的计费压力(现在情况在发生变化)现在的我们也许无法想像当年的那些牛人为什么会有如此差劲的设计,但是多考虑当时的背景,当时电信级的服务器性能可能还不如你使用的笔记本呢。这方面有个反例,当年我们有个经理是从IBM过来的,他的一句口头禅就是硬件能解决的问题都不是问题,看看IBM的websphere就知道他们对这句话理解有多深。
9 楼 nihongye 2007-12-28   引用
robbin 写道

范式带来的问题:

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


在最有效使用连接方式的前提下,多表关联是否还会导致低下的查询性能。一个符合完全3范式设计的数据库在查询中对于最有效连接方的使用是否可与充满冗余字段的数据库设计相比,感觉问题的关键在于磁盘IO,如果全表扫描,大家都一样;有足够内存,索引可以在内存命中;而计算连接又依赖于索引,如果连接的计算过程可以在内存完成,感觉连接不应该带来太多的性能差异,担心的倒是数据库引擎和人是否有足够智能控制查询的执行。
8 楼 Godlikeme 2007-12-28   引用
andyao 写道
引用
传统的数据库应用软件开发,程序员很难从符合三大范式的数据模型当中获得有效的查询性能。符合三大范式就意味着数据库表会拆分的很细,表间关联很多,统计分析查询就不可避免的导致n张表的联合查询,在没有有效的应用层缓存的情况下,这种查询无可避免的性能低下。这使得程序员宁肯违背三大范式,而选择查询性能优先的数据库设计。


这是关键点


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


7 楼 hypercube1024 2007-12-28   引用
leadyu 写道


金融不好说,接触的少。


现在做银行系统,似乎也没有哪个用面向对象的设计方法,基本都是数据表驱动的开发……
6 楼 gw187 2007-12-28   引用
多表复杂关联导致的瓶颈问题,可以使用物化视图来解决;通过使用空间来换速度好于改变设计换速度
5 楼 robbin 2007-12-27   引用
是不是我们可以这样说:

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

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

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

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

范式带来的问题:

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

看来范式和ORM还真是一对阿,连优点和缺点都一样的。
4 楼 robbin 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帮不上忙了,就需要额外的机制来保证。怎样权衡,还需要实践,就不是一次能够讲清楚的了。
3 楼 leadyu 2007-12-27   引用
确实,任何对象,通过key层层关联,确实最终都可以转换为二维关系。存储模型是可以匹配的,但是对象设计和关系设计在设计角度上确存在不可逾越的阻抗

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

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

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

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

确实,没错。

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

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

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

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

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

金融不好说,接触的少。
2 楼 leadyu 2007-12-27   引用
引用

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


好像理工专业都要学习数据库吧,看来robbin大哥那节课是逃课了,呵呵。
1 楼 andyao 2007-12-27   引用
引用
传统的数据库应用软件开发,程序员很难从符合三大范式的数据模型当中获得有效的查询性能。符合三大范式就意味着数据库表会拆分的很细,表间关联很多,统计分析查询就不可避免的导致n张表的联合查询,在没有有效的应用层缓存的情况下,这种查询无可避免的性能低下。这使得程序员宁肯违背三大范式,而选择查询性能优先的数据库设计。

 

原创粉丝点击