mongoDB中的数据模型设计

在mongoDB数据库中,BSON数据(被称作文档,可以理解为加强版的json)被存储在集合中,而这个“集合”的概念虽然与关系型数据库(例如mysql)的“表”有些类似,但却灵活很多。

在mysql这类关系型数据库中,我们必须先声明符合范式要求的二维表结构,通过建表语句规定数据属性,然后才能将符合格式的数据写入。

而集合更像一个来者不拒的黑盒子,格式完全不同的文档都可以写入。当然在实际开发中一般不会能把不同的文档写入同一个集合,因为这样的可读性太差。通常的做法是把同一类型结构相同的数据放入一个集合。

但是这些文档会随着嵌套层级加深变得愈发复杂,所以设计合理的数据模型来处理文档之间的关系变得尤为重要。

设计思路

处理文档之间的关系有两种方式:

引用。

举例,设计集合来存储老师、学生、班级信息。

已知一个老师给多个学生上课,一个老师也给多个班级上课。

这种情况下,老师、学生和班级的信息可以分别存储到不同集合,然后通过引用的方式来处理它们的关系。

//collection teachers
{
    _id: 123456,
    name: 'Ms Li'
}
//collection students
{
    _id: 22222,
    name: 'Xiao ming',
    teacher: [123456]
}
//collection class
{
    _id: 33333,
    name: 'class 1',
    teacher: [123456]
}

嵌套。

举例,设计集合来存储每个班级和学生的信息。

已知一个学校有多个班级,一个班级有多名学生。

单纯考虑班级和学生的话就可以用嵌套的方式来处理它们之间的关系。

// collection classes
{
    _id: 1111,
    name: 'class6-1',
    students: [{
        _id: 222,
        name: 'Han Meimei'
    }]
}

文档关系

什么时候该引用,什么时候该使用嵌套?可以根据文档之间的关系来分析~

一对一

当两个文档互为彼此的唯一的时候,就比较适合嵌套的方式来存储数据。

举个例子,如果要将各个Node.js项目的的配置文件package.json存入一个集合中,就属于这种场景。

{
  "name": "xxx",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "dependencies": {
    "koa": "^2.3.0",
    "koa-router": "^7.2.1",
    "mongoose": "^4.11.6",
    "request": "^2.81.0"
  },
  "devDependencies": {},
  "scripts": {
    "dev": "nodemon -L --harmony src/index.js",
    "launch": "pm2 start src/index.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}

其中"dependencies"、"scripts"这类属性就是一对一的嵌套关系。

多对多

对于这类关系一般都是毫无悬念地选择引用的方式了。前面提到的老师和学生的例子,这就就不重复举例了。

一对多

这类关系是相对复杂的,分情况来看。

如果子文档数量是有限的,且不需要被其他文档引用,那么适合嵌套。

举例,一个客户有多个收件地址,那么就可以设计成嵌套数组。

{
    "name": "老王",
    "address": [{
        "name": "家",
        "province": "北京",
        ......
    }, {
        "name": "公司",
        "province": "天津"
    }]
}

如果子文档数量是持续增长的,或者需要被其他文档引用,那么适合引用。

譬如说商品分类和商品详情,由于商品是会不断增长的,同时又需要经常被其他地方引用(如订单),所以最好设计成引用,将商品详情单独记录到一个集合。

设计原则

当然有可能面对的实际情况会比较复杂,或者属于多种情况,这个时候我们只要抓住这几个基本原则就可以了。

  • 文档增长性。对于持续增长的子文档,建议采用引用的方式,将子文档单独写入到一个集合。
  • 操作原子性。做到引用的单一性,不要双向引用,避免“一处变化,处处改动”的情况。
  • 集合可拆分。不同类型的数据建议拆分成不同集合,比如用同一个集合记录不同级别的日志不如用不同集合分别记录该级别的日志。一般来说尽量不要设计嵌套层数过多的集合。

发表评论