非规范化

附录 10.5

翻译进度

本章内容:

  • 理解非规范化是什么。
  • Mongo 与传统的关系型数据库的比较。
  • 了解什么时候你*不要*非规范化数据。
  • 非规范化数据不存储规范化的数据。换句话说非规范化意味着相同数据的多个拷贝同时存在。

    上一章中,我们在帖子中非规范化评论总数,以避免每次都加载所有的评论。在数据建模意义上说这是冗余的,因为我们可以通过计数每个评论,随时计算出该总数(当不考虑运行速度)。

    非规范化通常意味着额外的开发工作。在例子中,我们每次添加或删除评论时,还需要同时更新相关的帖子,以确保 commentsCount 字段保持准确。这就是为什么关系型数据库,比如 MySQL,对这种做法不以为然。

    但是,规范化做法也有其缺点:没有 commentsCount 项,就象开始我们做的那样,为了计算评论总数,每次我们都需要传输所有的评论。非规范化使我们能够完全避免这种情况。

    一份特殊发布

    我们可以创建一个特殊的发布,送出我们有兴趣的的评论数(通过聚合查询服务器,我们目前能看到帖子的评论数)。

    如果这样发布代码的复杂性不超过由非规范化造成的难度,它是值得考虑的…

    当然,这样的考虑是和应用相关的:如果你写的代码,数据完整性是非常重要的,那么避免数据的不一致,就比性能提升,更为重要和更有较高优先级。

    嵌入文件或使用多个集合

    如果您是有 Mongo 的经验,你可能会感到惊奇,我们给评论单独创建了第二个集合:为什么不直接在帖子文档中嵌入评论?

    事实证明当进行集合操作时,Mongo 提供的许多工具会给我们带来更好的结果。例如:

    1. {{#each}} helper 遍历游标(collection.find() 的结果)是非常有效的。但是当它遍历一个较大文件中的对象数组时,效率就不高。
    2. allowdeny 在 document 文档级别上操作,因此可以很容易地确保每个评论修改的正确性,但是在帖子级别就会变得更复杂。
    3. DDP 在文档的顶级属性级操作————这意味着,如果 commentpost 的一个属性,每当在帖子上创建一个新评论时,服务器就会将该帖子的整个更新的评论列表发送到每个连接的客户端。
    4. 在文档级别更容易控制发布和订阅。例如,我们想对帖子的评论进行分页,如果评论没有属于它们自己的集合,我们会发现很难做到。

    Mongo 推荐嵌入文档以减少昂贵的查询次数。然而,考虑到 Meteor 的架构,就不成问题了:大多数时候我们在客户端查询评论,其数据库访问基本上是没有的。

    非规范化的缺点

    也有很好的理由使你不要非规范化数据。为了更好地理解反对非规范化的情况,我们推荐阅读 Sarah Mei 写的为什么你不应该使用 MongoDB