• 进入"运维那点事"后,希望您第一件事就是阅读“关于”栏目,仔细阅读“关于Ctrl+c问题”,不希望误会!

数据库设计范式与反范式

MySQL 彭东稳 8年前 (2017-04-20) 29205次浏览 已收录 0个评论

数据库规范化,又称数据库或资料库的正规化、标准化,是数据库设计中的一系列原理和技术,以减少数据库中数据冗余,增进数据的一致性。关系模型的发明者埃德加·科德最早提出这一概念,并于1970年代初定义了第一范式、第二范式和第三范式的概念,还与Raymond F. Boyce于1974年共同定义了第三范式的改进范式——BC范式。

除外还包括针对多值依赖的第四范式,连接依赖的第五范式,DK范式和第六范式。

现在数据库设计最多满足3NF,普遍认为范式过高,虽然具有对数据关系更好的约束性,但也导致数据关系表增加而令数据库IO更易繁忙,原来交由数据库处理的关系约束现更多在数据库使用程序中完成。

数据库设计三大范式

为了建立冗余较小、结构合理的数据库,设计数据库时必须遵循一定的规则。在关系型数据库中这种规则就称为范式。范式是符合某一种设计要求的总结。要想设计一个结构合理的关系型数据库,必须满足一定的范式。

在实际开发中最为常见的设计范式有三个:

1.第一范式(确保每列保持原子性)

第一范式是最基本的范式,如果数据库表中的所有字段值都是不可分割的基本数据项,同一列中不能有多个值,即实体中的某个属性不能有多个值或者不能有重复的属性,就说明该数据库表满足了第一范式。

第一范式的合理遵循需要根据系统的实际需求来定,比如某些数据库系统中需要用到“地址”这个属性,本来直接将“地址”属性设计成一个数据库表的字段就行。但是如果系统经常会访问“地址”属性中的“城市”部分,那么就非要将“地址”这个属性重新拆分为省份、城市、详细地址等多个部分进行存储,这样在对地址中某一部分操作的时候将非常方便。这样设计才算满足了数据库的第一范式,如下表所示。

数据库设计范式与反范式

上表所示的用户信息遵循了第一范式的要求,这样在对用户进行城市分类的时候就非常方便了,也提高了数据库的性能。

2.第二范式(确保表中的每列都和主键相关)

第二范式在第一范式的基础之上更进一层,第二范式需要确保数据库表中的每一列都和主键相关,而不能只与主键的某一部分相关(主要针对联合主键而言)。也就是说在一个数据库表中,一个表中只能保存一种数据,不可以把多种数据保存在同一张数据库表中。

比如要设计一个订单信息表,因为订单中可能会有多种商品,所以要将订单编号和商品编号作为数据库表的联合主键,如下表所示。

数据库设计范式与反范式

这样就产生一个问题:这个表中是以订单编号和商品编号作为联合主键。这样在该表中商品名称、单位、商品价格等信息不与该表的主键相关,而仅仅是与商品编号相关。所以在这里违反了第二范式的设计原则。

而如果把这个订单信息表进行拆分,把商品信息分离到另一个表中,把订单项目表也分离到另一个表中,就非常完美了,如下所示。

数据库设计范式与反范式

这样设计,在很大程度上减小了数据库的冗余。如果要获取订单的商品信息,使用商品编号到商品信息表中查询即可。

3.第三范式(确保每列都和主键列直接相关而不是间接相关)

满足第三范式必须先满足第二范式。简而言之,第三范式需要确保数据表中的每一列数据都和主键直接相关,而不能间接相关。

比如在设计一个订单数据表的时候,可以将客户编号作为一个外键和订单表建立相应的关系。而不可以在订单表中添加关于客户其它信息(比如姓名、所属公司等)的字段。如下面这两个表所示的设计就是一个满足第三范式的数据库表。

数据库设计范式与反范式

这样在查询订单信息的时候,就可以使用客户编号来引用客户信息表中的记录,也不必在订单信息表中多次输入客户信息的内容,减小了数据冗余。

范式的优缺点

在解决数据库性能问题时,经常会被建议对schema进行范式化建议,尤其是写密集的场景。这通常是个好建议,范式话能带来这些好处:

  • 范式化的数据更新操作通常比反范式化要快,消除了数据冗余、更新异常、插入异常和删除异常。
  • 当数据较好地范式化时,就只有很少或者没有重复数据,所以只需要修改更少的数据。
  • 范式化的表通常更小,可以更好地放在内存里,所以执行操作会很快。
  • 很少有多余的数据意味着检索列表数据时更少需要distinct或者group by语句,在前面的例子中,需要使用distinct或者group by才能获得一份唯一的部门列表,如果部门是一种单独的表则只需要简单查询这张表就可以了。

当然范式化的设计并不是只带来好处,范式化的schema带来的缺点是需要关联,稍微复杂一些的查询语句在符合范式的schema上都可能需要至少一次关联,甚至多次关联。关联的代价是很昂贵的,也可能使一些索引策略无效。例如范式化可能将列存在不同的表中,而这些列如果在同一表中本可以属于同一个索引。

反范式化

反范式就是在通过增加冗余数据或数据分组来提高数据库读性能的过程。有时候反范式能掩盖关系型数据库软件的低效。反范式的schema因为数据都在一张表中,可以很好的避免关联。

如果不需要关联表,则对大部分查询最差的情况,即使表没有使用索引是全表扫描,当数据比内存还大的时候可能比关联表要快得多,因为这样避免了随机IO(扫描全表基本上是顺序IO)。使用反范式的表能使用更加有效的索引策略。

混合使用范式化和反范式化

完全的范式化和完全的反范式化schema都是实验室才有的东西:在真实世界不会有这么极端的使用,在实际应用中经常需要混用,可能使用部分范式化的schema、缓存表,以及其他技巧。

最常见的反范式化数据的方法是复制或者缓存,在不同的表中,存储相同的特定列。但是反范式化使得更新数据的代价变大了,需要考虑更新的频率以及更新的时常,并和执行select查询的频率进行比较。所以实际使用中,是否使用反范式化还是要根据具体需求确定,如果查询多余更新,可以反范式化多一些,如果需要经常更新数据,那么过多的反范式化列使得整体的性能反而降低了。

一般来说,在范式化达到一定的满意水平并且所需要的约束和规则都已经建立起来才进行反范式化。

从性能来说,范式化有更好的写性能,反范式化有更好的读性能。


如果您觉得本站对你有帮助,那么可以支付宝扫码捐助以帮助本站更好地发展,在此谢过。
喜欢 (3)
[资助本站您就扫码 谢谢]
分享 (0)

您必须 登录 才能发表评论!