MongoDB内嵌与引用

2018-01-13 10:53:37来源:网络收集作者:纳米程序员人点击

分享

阿里云爆款

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中,多和少可以权衡,作者和文章之间可能是一对少的关系:每个作者只有几篇文章。文章和评论之间是一对多的关系,每篇文章可以有很多条评论。“少”的关系使用内嵌的方式比较好,“多”的关系使用引用的方式比较好。


最新文章

123

最新摄影

微信扫一扫

第七城市微信公众平台