MongoDB内嵌与引用

MongoDB的范式化,是将数据分散到不同的集合中,不同的集合之间可以相互引用数据。如果需要修改某一块数据,只需要修改保存这块数据的文档即可,但查询时,需要在不同集合之间执行连接查询,需多次查询实现。
反范式化,将每个文档所需的数据都嵌入在文档内部,每个文档都拥有自己的数据副本,而不是所有文档共同引用同一个数据副本。如果信息发生了变化,所有相关的文档都要进行更新。但是执行查询时,只需要一次查询。
范式化可以提高数据写入速度,反范式化可以提高数据的读取速度。
学生选课的例子(一个学生选择多门课程):
如果建立一个学生集合,一个课程集合,一个关系集合:
关系表中数据类似这样结构的:

{
    "_id":"ObjectId("xxxxx")",
    "studentId":"ObjectId("xxxxx")",
    "classes":[
        ObjectId("xxxxxx"),
        ObjectId("xxxxxx"),
        ObjectId("xxxxxx")
    ]
}

每个关系,保存了对应的学生编号,以及课程的编号数组。实际中,如果想根据某个姓名查找选择的课程,需要三次查询:
1. 在学生信息集合中根据学生姓名查找学生ID;
2. 根据学生ID在关系表中查找对应的课程编号数组;
3. 根据课程数组中的编号查找对应的课程名。
如果将课程嵌入到学生文档中,即省去学生表,只保留学生课程关系表,和课程信息表,如:

{
    "_id":"ObjectId("xxxxx")",
    "name":"Zhang san",
    "classes":[
        ObjectId("xxxxxx"),
        ObjectId("xxxxxx"),
        ObjectId("xxxxxx")
    ]
}

这样,只需要两次查询即可,根据姓名查到课程编号,再根据课程编号查出对应的课程名即可。这种方式如果数据不需要随时访问,也不需要随时变化,那么就非常好。
如果进一步反范式化,可以将课程信息完全内嵌。这样一次查询即可,如:

{
    "_id":"ObjectId("xxxxx")",
    "name":"Zhang san",
    "classes":[
        {
            "class":"Math",
            "credits":"5",
            "room":"204"
        },
        {
            "class":"English",
            "credits":"5",
            "room":"305"
        },
        {
            "class":"Art",
            "credits":"2",
            "room":"201"
        }
    ]
}

这样的好处是一次查询即可得到学生的课程信息。但是会占用更多的存储空间,因为每个课程会多次出现在很多学生的选课中。同时,数据更新也困难,如果课程的学分,或者教室更改了,那么需要更新选了这门课程的所有学生的文档。
那么可以混合使用内嵌和引用,将课程中的详细信息抽到子文档中,常用信息仍然保留在内嵌中。

{
    "_id":"ObjectId("xxxxx")",
    "name":"Zhang san",
    "classes":[
        {
            "_id":"ObjectId("xxx")",
            "class":"Math"
        },
        {
            "_id":"ObjectId("xxx")",
            "class":"English"
        },
        {
            "_id":"ObjectId("xxx")",
            "class":"Art"
        }
    ]
}

这样,同样可以一次查询查出选择的课程。而且当课程的详细信息发生变化时,在课程信息集合中对应进行修改即可。如果希望在一个页面中包含更多(更少)的信息,就可以将更多(更少)的信息放在内嵌文档中。
另外需要考虑,信息更新频繁还是信息读取频繁。如果数据会定期更新,那么范式化是比较好的选择;如果数据变化不频繁,为了优化更新而牺牲读取效率就不划算了。
数据生成越频繁,越不应该将这些数据内嵌。如果内嵌字段或者内嵌字段数量是无限增长的,就应该把这些内容保存在单独的集合中,使用引用的方式进行访问,而不是内嵌到其他文档中。比如评论列表或者活动列表等信息,应该保存在单独的集合中。
如果某些字段是文档数据的一部分,就需要内嵌到文档,而如果查询文档时经常需要排除某个字段,该字段就应该放在另外的集合中。
MongoDB中,多和少可以权衡,作者和文章之间可能是一对少的关系:每个作者只有几篇文章。文章和评论之间是一对多的关系,每篇文章可以有很多条评论。“少”的关系使用内嵌的方式比较好,“多”的关系使用引用的方式比较好。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值