转载自:http://www.tsingsong.com/jforum/posts/list/763.page
这么10多年以来,我经历过很多的OLTP数据库模型设计,
我一直都遵从范式化的原则,设计高效合理,没有出过问题,而且开发人员易读易懂,十分顺利。
相反的,
我见过别人的一些项目,在开发前,没有统一的全面的进行数据库模型设计,
大家就匆忙动手开发了,在java开发的时候,觉得需要一个表存数据,就加一个表,一开始没觉得有问题,
随着开发的继续进行,加的表越来越多,开发人员之间的表之间有了冲突,功能之间所需的表也慢慢冲突了,
这时候,表混论了,开发不能再继续进行了,只能回过头来,重新全面的统一的设计完表的结构,所有开发人员都明白了这个结构之后,才能继续进行
——我曾经救过这样的场,深有感触!!!
——记得群里的李杰Aydge曾经说明:设计做不好,一辈子最维护!
那么,
【数据库模型设计难吗?】
我的回答是,OLTP数据库模型设计非常简单,只要你足够了解需求,能区分清楚系统存储的各个对象,以及他们之间的关系就可以了
【关键点是什么呢?】
设计前期:熟悉需求和业务;
设计中期:准确寻找实体型和他们的关系,时时刻刻运用范式化原则审核模型
设计后期:根据业务访问的性质,适当使用反范式化,以及,对大数据量表及大字段表进行拆分,分区表或者分表
【范式化重要吗?】
在OLTP的数据库建模中,三个基本范式是非常重要的,他是我们检验设计正确性的依据!
很多人,说范式化是理论的东西,在实际项目中用不到,其实这种说话是完全错误的!
或者说,这样的朋友,估计没有做过比较复杂的项目,做过的项目就几张表就能搞定的小项目,业务很简单,即使不遵守范式化,他也可以用使用复杂的程序进行维护!
【三个范式的内容是什么?】
三个范式的内容,在数据库大学教材里面,讲的很难,都是什么集合运算,使得大家望而生畏,感觉很难,
其实,在充分读懂那些文绉绉的天书之后,你会发现,范式化知识其实非常简单,我简单用大白话记录在这里:
【第一范式】:一个表的列具有原子性,那么就满足第一范式
这句话,可以说成:一个表的列,在系统中,不会被局部访问,也就是,这个列不会被拆分开研究了,
还可以说成:一个实体型的属性,不能再被拆分成多个子属性,属性在数据库中就是表的列了
这是什么意思呢?
比如说:公司员工管理系统中的“员工表”中的“姓名”这一列,就不需要在拆分成“姓”和“名”两列了,因为在这个系统中,我们对姓名总是一次性读取存储
但是,如果实在一个通过人的姓名进行算命的命理推算系统中,就需要把“姓”和“名”分拆成两列,因为在这样的系统中,对“姓”是需要特别研究的,同样对“名”也需要单独分析。
www.eeqee.com
上面的这个例子,很明确的说明了第一范式的把握尺度,就是实际的需求需要情况。
【第二范式】:一个表中的非主属性对主属性的依赖是完全依赖,不存在部分依赖,那么就满足第二范式
这句话,还可以说成:一个表的非主键列,对主键列的的依赖必须是完全依赖的。
这里需要强调的是,对于使用单独一列做主键的表中,非主键列对主键列的依赖,肯定是完全依赖的,
所以,第二范式所讲的非完全依赖情况,肯定只会出现在由多个列形成“复合主键”的表中!
这是什么意识呢?
比如有一个生育情况表,首先假设一对男女只能生育一个孩子,
这个表有这么几个字段:
(男人,女人,结婚时间,孩子,男人父亲,男人母亲,女人父亲,女人母亲),其中(男人,女人)是复合主键,
现在我们分析一下其他三个字段对(男人,女人)复合主键的依赖情况:
(结婚时间):对(男人,女人)是完全依赖的,因为结婚这件事情,不是一个人可以进行的,需要男人和女人的共同参与;
(孩子):对(男人,女人)是完全依赖的,因为生孩子这件事情,不是一个人可以进行的,需要男人和女人的共同参与;
(男人父亲):对(男人,女人)是部分依赖,(男人父亲)只是依赖于(男人),而和(女人)无关,有没有这个(女人),(男人)都肯定有他的父亲
其他三列(男人母亲,女人父亲,女人母亲)同理,也是部分依赖于(男人,女人),所以他们违反了第二范式,需要将他们从该表中取出,单独设计表进行存储
特别强调一下:
如果对实体表严格采用自增数字的单列作为主键,那么违反第二范式的情况,只可能出现在关系表中,因为关系表的主键是有实体表的主键复合而成的!
【第三范式】:非主属性对主属性的依赖不存在传递依赖,那么就满足第三范式
还可以说成:一个表的非主属性列对主键的依赖,不存在传递依赖,那么就满足第三范式
这是什么意思呢?
假设刚才生育情况表,改成如下:
(男人,女人,结婚时间,孩子,孩子的班主任老师),同样适用(男人,女人)作为复合主键
我们之前分析过,结婚时间和孩子两列对复合主键(男人,女人)依赖是完全依赖,而且也是直接依赖,因为(男人,女人)可以直接决定他们,
但是对于 (孩子的班主任老师)这一列,对(男人,女人)的依赖是传递依赖,因为没有(孩子)就没有(孩子的班主任老师),所以(孩子的班主任老师)对(男人,女人)的依赖是先依赖于(孩子),再通过(孩子)对(男人,女人)的依赖,来间接的实现对(男人,女人)的依赖,这个依赖是传递依赖,
这样,(孩子的班主任老师)放在这个表中,就违��了第三范式,为此,我们要将该列移除该表,放到该放的其他表中。
特别强调一下:
如果对实体表严格采用自增数字的单列作为主键,那么违反第三范式的情况,往往只会发生在含有外键列的子表中,常常我们会错误的把依赖于外键列的列放到子表表中,这就违反了第三范式!
www.eeqee.com
再说明一下:
这里所说的存在传递依赖的列,应该是父表的列,放在子表中,会导致数据冗余,插入,更新异常!
比如:
父表是“学校表”,“主键”是“学校id”,有个字段是“学校名字”,
子表是“班级表”,现在“学校id”在班级表中做“外键”,表示班级所属的学校的意思,
此时,如果我们设计的时候,看到“学校id”在班级表中,就盲目的将“学校名字”这个列放入到“班级表”中了,
那么,就错了,很明显的错误有两个:
(1)插入异常,当现有学校,还未分班时,班级表中没有记录,那学校名字就存不了了,
(2)数据冗余:一个学校一个名字,其实存一次就行了,但是一个学校多了班级。
【违反范式会怎样?】
在OLTP系统中,违反三个范式会造成数据的增删改错误,和存储的冗余,导致数据更新异常,不同步,数据不一致!
所以,在OLTP系统中,切记要满足范式化,只有在局部,需要性能的考虑的时候,才可以做适当的反范式。
【什么是反范式】?
在OLTP系统中,在进行范式化之后,实体型和他们的关系,以及属性之间的关系非常的清晰了,但是表的拆分很多,对一些多表关联的情况,性能也是一个问题,
为了性能的考虑,需要进行适当的反范式化,就是故意违反范式化,多加一些字段,来减少表的关连查询。
【反范式】=【反】+【范式】
从上面的公式可以看出,反范式是在进行了范式话之后,通过“反”逆向来“添加字段”,不是移动字段,实现反范式的,
反范式的关键是,保证数据的一致性有点困难,需要做严密的设计,所以,请小心使用反范式!
切记,范式化是原则问题,必须要先做,做好!反范式是选择性做法, 为了性能罢了!
范式化是对于错的问题,反范式化是快运慢的问题!我们要先做对了,才能做快了!
对于反范式化,可以通过其他方法避免,比如索引,分区表,搞性能服务器,都是提高性能的手段
看完上面的资料,怎么样?三个范式是不是很简单?!
——很多人,忙着去考OCP,其实,这些才是最最重要的,没有这些,项目不可能顺利进行,这些数据库原理的知识,请大家务必重视!