范式与反范式的应用(一)
在数据库设计中范式的应用是一个永恒的话题,从一开始学关系型数据库设计开始,老师就会对我们说在进行数据库的表结构设计时,运用范式会有多么重要的意义,确实,在实际工作当中你也会发现范式确实非常重要,但是随着工作的深入,你会慢慢发现有时候遵守范式反而会让你掉入一个又一个陷阱,于是我们又会谈到一个反范式的概念,什么时候需要遵守范式,什么时候又需要反范式,笔者试图利用几年开发的经验,结合大众点评网的实际例子,来跟大家分享一下这个过程。
总体来说,对于一个公共型网站的数据库的设计往往会经历三个阶段,第一阶段:遵守范式;第二阶段:反范式;第三阶段:结合范式与反范式,灵活应用。
今天想先讲讲第一阶段,举几个点评网数据库设计的例子来讲一下如何来遵守范式进行数据库设计的。
第一范式(1NF)
所谓第一范式(1NF),是指数据库表的每一列都是不可分割的基本数据项,同一列中不能有多个值,即实体中的某个属性不能有多个值或者不能有重复的属性。如果出现重复的属性,就可能需要定义一个新的实体,新的实体由重复的属性构成,新实体与原实体之间为一对多关系。在第一范式(1NF)中表的每一行只包含一个实例的信息。
一般讲,只要是关系型数据库,一般都会遵守第一范式,但是事情往往没有那么简单,在进行数据库设计的时候,你真的都考虑清楚每个字段都是不可分割的了吗?就拿点评网的商户信息表来说,有一个字段叫“商户名”,如果要添加两家新商户,比如“麻辣风暴”和“豆捞坊天目西路店”,那就会添加两条记录,其中一条的商户名为“麻辣风暴”,而另外一条记录的商户名为“豆捞坊天目西路店”,那么这个就不符合第一范式了,因为其实这个信息是可以分割的,其中“豆捞坊”是总店名,而“天目西路店”是分店名。所以根据第一范式的设计,应该分拆为两个字段即商户名和分店名。
第二范式(2NF)
所谓第二范式(2NF),是指数据库表中不存在非关键字段对任一候选关键字段的部分函数依赖(部分函数依赖指的是存在组合关键字中的某些字段决定非关键字段的情况),也即所有非关键字段都完全依赖于任意一组候选关键字。
这些定义总是非常的晦涩难懂,还是要靠举例说明才能比较清晰。
我们用点评网社区的部落成员表来举例,部落成员表的作用是定义每个部落的成员信息:
部落名称 会员名 会员等级 加入时间 部落角色
显然这个表有两个关键字段,即部落名称和会员名,其中会员等级这个非关键字段依赖于会员名这个关键字段,不以该会员是否加入该部落而改变,其性质不同于加入时间和部落角色,所以这样的设计是不符合第二范式的,为了遵守第二范式,我们需要将该表分拆为两个表:
会员ID 会员名 会员等级
部落名称 会员ID 加入时间 部落角色
第三范式(3NF)
在第二范式的基础上,数据表中如果不存在非关键字段对任一候选关键字段的传递函数依赖则符合第三范式。所谓传递函数依赖,指的是如果存在"A → B → C"的决定关系,则C传递函数依赖于A。
还是拿点评网的商户信息表来举例,点评的商户一般都由会员来添加,这样商户信息表内就会包含以下信息:
商户名 分店名 会员名 会员等级
如果商户信息表是这样设计的,虽然会员名并非是一个关键字段,但是这不符合第三范式了,因为其中的会员等级两个字段和会员名这个字段有函数依赖的关系。所以根据第三范式,应该将这个表分拆为两个表,即商户信息表和会员信息表。
会员ID 会员名 会员等级
商户名 分店名 会员ID
希望以上的几个例子能帮助读者理解三个范式的含义,当然,当你的网站到了一定的规模以后,你会发现,完全按照这样的范式来进行设计,那你就被忽悠了,被忽悠了怎么办?请关注本博客,下次将通过这些例子的演化来说明什么情况下我们需要来反范式。