数据库的范式化和反范式化模型

在实际工作中,数据库数据模型的建立和选择对数据的“增删改查”具有直接影响,从基本角度来讲,数据模型具有“范式化”和“反范式化”两种,而在实际工作场景中,常依赖实际情况采取这两种模型混用。

范式化

范式化模型要求满足下面三大范式:
1. 每个字段只包含最小的信息属性。如果某个字段名称为name-age,value为zhangsan-23,则这个模型不满足第一范式,需要将name-age分为两个属性name和age后才满足第一范式。
2. (在满足第一范式基础上)模型含有主键,非主键字段依赖主键。比如订单这个模型,它的主键是订单ID,那么订单模型其它字段都应该依赖于订单ID,如商品ID和订单没有直接关系,则这个属性不应该放到订单模型而应该放到”订单-商品”中间表。
3. (在满足第二范式基础上)模型非主键字段不能相互依赖。订单表(订单编号,定购日期,顾客编号,顾客姓名,……),初看该表没有问题,满足第二范式,每列都和主键列”订单编号”相关,再细看你会发现”顾客姓名”和”顾客编号”相关,”顾客编号”和”订单编号”又相关,最后经过传递依赖,”顾客姓名”也和”订单编号”相关。为了满足第三范式,应去掉”顾客姓名”列,放入客户表中。
示例:

{
     "_id" : ObjectId("5124b5d86041c7dca81917"),
     "title" : "如何使用MongoDB", 
      "author" : [ 
               ObjectId("144b5d83041c7dca84416"),
              ObjectId("144b5d83041c7dca84418"),
              ObjectId("144b5d83041c7dca84420"),
     ]
 }    

将作者(comment) 的id数组作为一个字段添加到了图书中去。这样的设计方式是在关系型数据库中常用的,也就是我们所说的范式化设计。在MongoDB中我们将与主键没有直接关系的图书单独提取到另一个集合,用存储主键的方式进行关联查询。当我们要查询文章和评论时需要先查询到所需的文章,再从文章中获取评论id,最后用获得的完整的文章及其评论。在这种情况下查询性能显然是不理想的。但当某位作者的信息需要修改时,范式化的维护优势就凸显出来了,我们无需考虑此作者关联的图书,直接进行修改此作者的字段即可。

反范式化

反范式化模型即不满足范式化的模型。
示例:

{
       "_id" : ObjectId("5124b5d86041c7dca81917"),
       "title" : "如何使用MongoDB",
       "author" : [
                {
                         "name" : "丁磊"
                         "age" : 40,
                         "nationality" : "china",
                },
                {
                         "name" : "马云"
                         "age" : 49,
                         "nationality" : "china",
                },
                {
                         "name" : "张召忠"
                         "age" : 59,
                         "nationality" : "china",
                },
      ]
  }     

在这个示例中我们将作者的字段完全嵌入到了图书中去,在查询的时候直接查询图书即可获得所对应作者的全部信息,但因一个作者可能有多本著作,当修改某位作者的信息时时,我们需要遍历所有图书以找到该作者,将其修改。

范式化与反范式化混用

即遵循一部分范式化设计规则。
示例:

{
       "_id" : ObjectId("5124b5d86041c7dca81917"),
       "title" : "如何使用MongoDB",
       "author" : [ 
               {
                         "_id" : ObjectId("144b5d83041c7dca84416"),
                         "name" : "丁磊"
                },
                {
                         "_id" : ObjectId("144b5d83041c7dca84418"),
                         "name" : "马云"
                },
                {
                         "_id" : ObjectId("144b5d83041c7dca84420"),
                         "name" : "张召忠"
                },
      ]
  }

这次我们将作者字段中的最常用的一部分提取出来。当我们只需要获得图书和作者名时,无需再次进入作者集合进行查询,仅在图书集合查询即可获得。
  这种方式是一种相对折中的方式,既保证了查询效率,也保证的更新效率。但这样的方式显然要比前两种较难以掌握,难点在于需要与实际业务进行结合来寻找合适的提取字段。如同示例3所述,名字显然不是一个经常修改的字段,这样的字段如果提取出来是没问题的,但如果提取出来的字段是一个经常修改的字段(比如age)的话,我们依旧在更新这个字段时需要大范围的寻找并依此进行更新。
  在上面三个示例中,第一个示例的更新效率是最高的,但查询效率是最低的,而第二个示例的查询效率最高,但更新效率最低。所以在实际的工作中我们需要根据自己实际的需要来设计表中的字段,以获得最高的效率。

范式化与反范式化比较

模型 优点 缺点
范式化模型 数据无冗余,便于更新 不利于查询操作
反范式化模型 便于查询 数据冗余,不利于更新

RDBMS(关系型数据库)模型设计过程中,常常使用范式约束我们的模型,但在NOSQL(非关系型数据库)模型中则大量采用反范式,在实际工作场景中,更多的是两者混用。


总结

由上可见一个良好而实用的数据模型往往是依赖于具体的需求场景的,在设计数据模型之前,仔细分析需求场景,不仅能提高效率,也能有效规避后期可能遇到的一些意外麻烦(这点有切身之痛)。

参考

开发高性能的MongoDB应用—浅谈MongoDB性能优化:http://www.cnblogs.com/mokafamily/p/4102829.html
数据模型的范式化和反范式化简述:http://heipark.iteye.com/blog/1914267

你可能感兴趣的:(mongodb,数据库)