mongodb 的 schema 设计方法

mongodb 的schema设计方法

前言

mongodb是NoSQL的代表,从使用关系型数据库(MySQL)到使用非关系型数据库(mongodb),其中的一些以前的设计的思维惯性总是在不知不觉的影响着自己的决策。设计的思想有共同之处,也有很大的不同。mongodb的优势在于他表示数据的方式非常丰富。下面就来总结一些设计的原则和方法。

原则

schema的设计最重要的不是当前设计的可扩展性,对设计的可读性,还是说原来的设计三范式。最重要的在于,你的app一般展现出来的数据是什么结构,就设计成什么样。取出即用。举个例子来说,如果设计一个博客的schema,按照原来的方法, 你可能会设计成:

posts
{
  _id: ,
  title: ,
  body: ,
  author: ,
  date: 
}

comments
{
  _id: ,
  post_id: ,
  author: ,
  order: 
}

tags
{
  _id: ,
  tag: ,
  post_id:
}

但是更好的设计是:

{
  _id: ,
  author: ,
  body: ,
  comments : [
    {
      body: ,
      email: ,
      author: ,
    },
    ...
    {
     ...
    }
  ],
  date: ,
  tags: [
    ...
  ],
  title: 
}

原来的设计范式的目标

  1. 尽可能的减少数据库数据修改的难度(减少数据冗余)

  2. 最小化数据库设计扩展的改动

  3. 避免数据库访问时的歧义

在mongodb中:

  1. 默认的来说设计的时候是要避免数据冗余的

  2. 不存在这样的问题,因为mongo中的schema非常灵活,你可以随时的改动

  3. 由于设计的时候就尽可能的按照应用需要的数据的形式设计,取出即用,所以第三个问题出现的概率也比较少

没有约束怎么办?

在mongo中,最经常思考的问题就是,没有外键怎么保持数据一致性?正如上述博客的第一种设计中,你在新插入一个评论的时候,数据库是不会保证你这个post_id是不是真在在posts这个collection里面有对应值。

解决的办法就是像第二种设计中的做法,把他嵌入在posts这个collection.由于这个时候评论已经是post的一部分,就再也不用担心插入的评论没有对应到一篇博客的问题。

没有事务怎么办?

在目前,mongodb是不支持事务的。也就是说,如果你一个业务需要修改好几条记录,你是没办法保证当其中一个操作失败的以后将其它操作回滚的,这种时候又应该怎么办?

虽然mongo没有提供事务,但是他提供了非常丰富的原子操作,我们应该充分利用这一点。在关系型数据库中,你可能有几张表,然后要通过join的方式去链接。所以你需要事务去同时修改几张表。但是在mongo中,在设计的时候你已经prejoin了(就是你已经把它们都嵌入在了同一个collection),你只需要直接一次修改整个post就可以实现事务的效果。

总的来说,解决的办法有三个:

  1. 重建你的设计,使得你能通过原子操作一次把它们都修改完

  2. 在你的软件中实现锁的机制,用寻找和修改写一系列的测试来实现。

  3. 在大量的数据或者不严格的场景中,容忍这种错误

典型的设计场景

一对一的关系

比如说应聘者和简历的关系。除非嵌在一起会导致你的数据大于16MB(mongo的限制),你都应该嵌在一起。做好的做法就是将一个比较少使用的嵌入一个经常食用的当中。

一对多的关系

比如说城市和人的关系,博客和评论

对于像城市和人的关系这样的,一个城市实在是对应了太多太多的人。如果将人嵌入在城市中,不太合适。在城市信息嵌入在人中就更不合适了,因为还会产生大量数据冗余,更新信息也特别麻烦。这种情况下最好的做法就是分开两个collection,然后人的collection中每一条都有一个city字段,来对应城市collection中的一条。

如果是像博客和评论这样一个对应的不是特别多的时候,最好的最法还是嵌进去

多对多的关系

例如书和作者的关系,老师和学生的对应关系

对于像书和作者这样,比较少对应比较少的,一个可行的做法就是分开两个collection。书collection中存一个authors数组,作者 collection中存一个books数组,互相对应。这种做法不好的地方就在与要手动维护数据一致性。另一个做法就是嵌在一起,这样会有性能的提升,不过这种做法会导致数据冗余,看具体的情况来取舍。特别的像老师和学生这种关系,最好最好就是不要将老师嵌在学生中,因为很可能一个新来的老师就还没有学生,这样你就没办法把这位老师加入到系统中。

树形结构

一个典型的场景就是像amazon这样的电商,一个商品分类下可能有很多个子分类。

其中的一个做法就是建立一个分类的collection,然后每个分类有一个parent_id字段,但是这样不便于找到他所有的祖先。所以比较好的做法是再加一个ancesters的数组字段,记录他所有的祖先的id,这样就能方便的查询到他的祖先和后代。

嵌入的优势

  • 提高读的效率。这意味着你要获取数据只需要查询一次数据库就行了。

处理大文件

如果一个数据量特别大(大于16M),比如读入一个100多M的mp4文件。这种情况下就需要用到GRIDFS.原理就是把他分割成一个一个小的块

相关推荐