- 超键∶能唯一标识元组的属性集叫做超键
- 候选键︰如果超键不包括多余的属性,那么这个超键就是候选键
- 主键:用户可以从候选键中选择一个作为主键
- 外键:如果数据表R1中的某属性集不是R1的主键,而是另一个数据表R2的主键,那么这个属性集就是数据表R1的外键
- 主属性:包含在任一候选键中的属性称为主属性
- 非主属性:与主属性相对,指的是不包含在任何一个候选键中的属性
第一范式
第一范式主要是确保数据表中每个字段的值必须具有原子性,原子性是主观的要结合真实业务
也就是说数据表中每个字段的值为不可再次拆分的最小数据单元
在设计某个字段的时候,对于字段X来说,不能把字段X拆分成字段X-1和字段X-2。事实上,任何的DBMS都会满足第一范式的要求,不会将字段进行拆分。
举例:user 表的设计不符合第一范式
其中,user_info字段为用户信息,可以进一步拆分成更小粒度的字段,不符合数据库设计对第一范式的要求。将user_info拆分后如下
第二范式
要满足数据表里的每一条数据记录,都是可唯一标识的。而且所有非主键字段,都必须完全依赖主键,不能只依赖主键的一部分
举例:
(球员编号, 比赛编号) → (姓名, 年龄, 比赛时间, 比赛场地,得分)
应该改成这样
(球员编号) → (姓名,年龄)
(比赛编号) → (比赛时间, 比赛场地)
(球员编号,比赛编号)→ (得分等属性)
对于非主属性来说,并非完全依赖候选键。这样会产生怎样的问题呢?
数据冗余 :如果一个球员可以参加 m 场比赛,那么球员的姓名和年龄就重复了 m-1 次。一个比赛
- 也可能会有 n 个球员参加,比赛的时间和地点就重复了 n-1 次。
- 插入异常 :如果想要添加一场新的比赛,但是这时还没有确定参加的球员都有谁,那么就没
法插入。 - 删除异常 :如果我要删除某个球员编号,如果没有单独保存比赛表的话,就会同时把比赛信息删
除掉。 - 更新异常 :如果调整了某个比赛的时间,那么数据表中所有这个比赛的时间都需要进行调整,否则就会出现一场比赛时间不同的情况
第三范式
确保数据表中的每一个非主键字段都和主键字段直接相关,也就是说,要求数据表中的所有非主键字段不能依赖于其他非主键字段。
商品类别名称依赖于商品类别编号,不符合第三范式。
表1:符合第三范式的 商品类别表 的设计
表2:符合第三范式的 商品表 的设计
三范式小结
关于数据表的设计,有三个范式要遵循
- 第一范式(1NF),确保每列保持原子性
数据库的每一列都是不可分割的原子数据项,不可再分的最小数据单元,而不能是集合、数组、记录等非原子数据项 - 第二范式(2NF),确保每列都和主键完全依赖
尤其在复合主键的情况下,非主键部分不应该依赖于部分主键 - 第三范式(3NF)确保每列都和主键列直接相关,而不是间接相关
范式的优点:
- 数据的标准化有助于消除数据库中的数据冗余,第三范式(3NF)通常被认为在性能、扩展性和数据完整性方面达到了最好的平衡。
范式的缺点:
- 范式的使用,可能降低查淘的效率。因为范式等级越高,设计出来的数据表就越多、越精细,数据的冗余度就越低,进行数据查询的时候就可能需要关联多张表,这不但代价昂贵,也可能使一些索引策略无效,
范式只是提出了设计的标准,实际上设计数据表时,未必一定要符合这些标准。开发中,会出现为了性能和读取效率违反范式化的原则,通过增加少量的冗余或重复的数据来提高数据库的读性能,减少关联查询、join表的次数,实现空间换取时间的目的。因此在实际的设计过程中要理论结合实际,灵活运用
反范式化
有的时候不能简单按照规范要求设计数据表,因为有的数据看似冗余,其实对业务来说十分重要。这个时候,就要遵循业务优先的原则,首先满足业务需求,再尽量减少冗余
。
如果数据库中的数据量比较大,系统的UV和PV访问频次比较高,则完全按照MySQL的三大范式设计数据表,读数据时会产生大量的关联查询,在一定程度上会影响数据库的读性能。如果想对查询效率进行优化,反范式优化也是一种优化思路。此时,可以通过在数据表中增加冗余字段来提高数据库的读性能
。
规范 vs 性能
- 为满足某种商业目标 , 数据库性能比规范化数据库更重要
- 在数据规范化的同时 , 要综合考虑数据库的性能
- 通过在给定的表中添加额外的字段,以大量减少需要从中搜索信息所需的时间
- 通过在给定的表中插入计算列,以方便查询
举例:
员工表和部门表之间,我们常常要通过员工查询其部门名称,这时可以将部门名称加入到员工表,虽然冗余但高频查询会减少表的连接
反范式的新问题
- 存储空间变大了
- 一个表中字段做了修改,另一个表中冗余的字段也需要做同步修改,否则 数据不一致
- 若采用存储过程来支持数据的更新、删除等额外操作,如果更新频繁,会非常 消耗系统资源
- 在 数据量小 的情况下,反范式不能体现性能的优势,可能还会让数据库的设计更加 复杂
反范式的适用场景
- 增加冗余字段的建议
- 增加冗余字段一定要符合如下两个条件。只有满足这两个条件,才可以考虑增加冗余字段。
- 这个冗余字段不需耍经常进行修改
- 这个冗余字段查询的时候不可或缺
- 历史快照、历史数据的需要
-
在现实生活中,经常需要一些冗余信息,比如订单中的收货人信息,包括姓名、电话和地址等。每次发生的订单收货信息都属于历史快照,需要进行保存,但用户可以随时修改自己的信息,这时保存这些冗余信息是非常有必要的
-
消息通知
- 当用户发出申请,将会发送一条消息,这条消息的状态应该是独立的字段,不应该关联申请表查询。
- 因为当申请被审批后,会再次发送内容相同但是状态和提示不同的消息,如果关联了申请表的申请状态,那么两次消息的状态将都会变成 已审批。
息的状态应该是独立的字段,不应该关联申请表查询。
- 因为当申请被审批后,会再次发送内容相同但是状态和提示不同的消息,如果关联了申请表的申请状态,那么两次消息的状态将都会变成 已审批。