test
场景一:学生表和课程表
存法一:引用型文档
学生表
{
"_id":"1",
"name":"zhang",
"course": [ "c1","c2","c3"]
}
课程表
{
"_id":"c1",
"course_count":40,
"name":"毛概"
}
缺点:关联或者两次查询,查询效率低。
优点:如果毛概课时从40,改为10,只需要修改毛概这条记录的值就行,不用修改学生信息。
存法二:内嵌型文档
{
"_id":"1",
"name":"zhang",
"course":[
{"course_count":40,"name":"毛概"},{"course_count":20,"name":"体育"},{"course_count":30,"name":"计算机"}
]
}
缺点:如果毛概的课时从40,改为了10,那么所有学生表里面的字段都给改值,太麻烦。
优点:一次性都能查出来值。
存法三:
为了解决存法一和存法二的缺点
学生表
{
"_id":"1",
"name":"zhang",
"course":[
# 存_id为了查询和修改其他数据,name等一般不变存入student信息中即可
{"name":"毛概","_id":"c1"},{"name":"体育","_id":"c2"},{"name":"计算机","_id":"c3"}
]
}
课程表
{
"_id":"c1",
"course_count":40,
"name":"毛概"
}
考虑:更新频繁和查询频繁的字段
内嵌文档和引用文档的区别
适合内嵌 | 适合引用 |
---|---|
子文档小 | 子文档大 |
数据不会定期改变 | 数据会定期改变 |
最终数据一致即可 | 中间阶段的数据必须一致 |
文档数据小幅增加 | 文档数据大幅增加 |
数据通常需要执行二次查询才能获得 | 数据通常不包含在结果中 |
快速读取 | 快速写入 |
假如我们有一个用户集合。下面是一些可能需要 的字段,以及它们是否应该内嵌到用户文档中。
用户首选项(account preferences):用户首选项只与特定用户相关,而且很可能需要与 用户文档内的其他用户信息一起查询。所以 用户首选项应该内嵌到用户文档中。
最近活动(recent activity):这个字段取决 于最近活动增长和变化的频繁程度。如果这 是个固定长度的字段(比如最近的10次活 动),那么应该将这个字段内嵌到用户文档 中。
好友(friends):通常不应该将好友信息内 嵌到用户文档中,至少不应该将好友信息完 全内嵌到用户文档中,所有由用户产生的内容:不应该内嵌在用 户文档中。
基数
常见的关系:一对一,一对多,多对多
假如有一个博客应用程序。
每篇博 客文章(post)都有一个标题(title),这是一 个一对一的关系。
每个作者(author)可以有多篇文章,这是一个一对多的关系。
每篇文章可以 有多个标签(tag),每个标签可以在多篇文章中 使用,所以这是一个多对多的关系。
在MongoDB中,many(多)可以被分拆为两个子 分类:many(多)和few(少)。
假如,作者和 文章之间可能是一对少的关系:每个作者只发表了为数不多的几篇文章。
博客文章和标签可能是多对少的关系:文章数量实际上很可能比标签数量多。
博客文章和评论之间是一对多的关系:每篇文章都可以拥有很多条评论。
只要确定了少与多的关系,就可以比较容易地在 内嵌数据和引用数据之间进行权衡。通常来 说,“少”的关系使用内嵌的方式会比较好,“多”的关系使用引用的方式比较好。
高度关联数据处理方式
很多社交类的应用程序都需要链接人、内容、粉丝、好友,以及其他一些事物。
对于这些高度关联的数据使用内嵌的形式还是引用的形式不容易权衡。
通常,关注、好友或者收藏可以简化为一个发布-订阅系统:
一个用户可以订阅另一个用户相关的通知。
这样,有两个基本操作需要比较高效:如何保存订阅者,如何将一个事件通知给所有订阅者。
如何设计订阅发布系统
方式一:内容生产者嵌套到内容订阅者的文档中。
用户表
{
_id:1,
name:zhang,
# 将内容生产者消息的id嵌套到列表中。关注的内容列表
flowing:[ObjectId("51250a72d86041c7dca81910"), ObjectId("51250a7ed86041c7dca81936"]
}
活动信息
{
_id:ObjectId("51250a72d86041c7dca81910"),
time:2021-10-23
address:西安南门外
info:户外音乐节
}
查询用户感兴趣的活动:比较容易,lookup
查询对这条活动感兴趣的所有用户,指定活动id在flowing列表里。
方式二:内容订阅者嵌套到内容生产者的文档中。
用户表
{
_id:1,
name:zhang
}
活动信息
{
_id:ObjectId("51250a72d86041c7dca81910"),
time:2021-10-23
address:西安南门外
info:户外音乐节,
# 存放关注活动的用户id,内容生产者存储内容消费者的信息
flowwer:[1, 2, 3, 4, 5]
}
查询对这条活动感兴趣的所有用户,比较容易。当这个生产者新发布一条信息时,我们立即就可 以知道需要给哪些用户发送通知。
查询用户感兴趣的活动:查询整个活动信息集合,判断用户id是否在flowwer列表里面。
方式三:
用户表
{
_id:1,
name:zhang
}
用户活动关联表
{
_id:xxxxxx
flowing:[ObjectId("51250a72d86041c7dca81910"), ObjectId("51250a7ed86041c7dca81936"]
}
活动信息
{
_id:ObjectId("51250a72d86041c7dca81910"),
time:2021-10-23
address:西安南门外
info:户外音乐节,