最新文章专题视频专题问答1问答10问答100问答1000问答2000关键字专题1关键字专题50关键字专题500关键字专题1500TAG最新视频文章推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37视频文章20视频文章30视频文章40视频文章50视频文章60 视频文章70视频文章80视频文章90视频文章100视频文章120视频文章140 视频2关键字专题关键字专题tag2tag3文章专题文章专题2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章专题3
当前位置: 首页 - 科技 - 知识百科 - 正文

MongoDB中的范式与反范式

来源:动视网 责编:小采 时间:2020-11-09 13:10:26
文档

MongoDB中的范式与反范式

MongoDB中的范式与反范式:本文摘录自《MongoDB权威指南》第八章,可以彻底回答以下两个问题: http://segmentfault.com/q/1010000000364944 http://segmentfault.com/q/1010000000364944 数据表示的方式有很多种,其中最重要的问题之一就是在多大程度上对数据进行范式化。范式化(n
推荐度:
导读MongoDB中的范式与反范式:本文摘录自《MongoDB权威指南》第八章,可以彻底回答以下两个问题: http://segmentfault.com/q/1010000000364944 http://segmentfault.com/q/1010000000364944 数据表示的方式有很多种,其中最重要的问题之一就是在多大程度上对数据进行范式化。范式化(n


很多社交类的应用程序都需要链接人、内容、粉丝、好友,以及其他一些事物。对于这些高度关联的数据使用内嵌的形式还是引用的形式不容易权衡。这一节会介绍社交图谱数据相关的注意事项。通常,关注、好友或者收藏可以简化为一个发布、订阅系统:一个用户可以订阅另一个用户相关的通知。这样,有两个基本操作需要比较高效:如何保存订阅者,如何将一个事件通知给所有订阅者。

比较常见的订阅实现方式有三种。第一种方式是将内容生产者内嵌在订阅者文档中:

{
 "_id": ObjectId("..."),
 "username": "batman",
 "email": "batman@waynetech.com",
 "following": [
 ObjectId("..."),
 ObjectId("...")
 ]
}

现在,对于一个给定的用户文档,可以使用形如db.activities.find({"user": {"$in": user["following"]}})的方式查询该用户感兴趣的所有活动信息。但是,对于一条刚刚发布的活动信息,如果要找出对这条信息感兴趣的所有用户,就不得不查询所有用户的“following”字段了。

另一种方式是将订阅者内嵌到生产者文档中:

{
 "_id": ObjectId("..."),
 "username": "joker",
 "email": "joker@mailinator.com",
 "followers": [
 ObjectId("..."),
 ObjectId("..."),
 ObjectId("...")
 ]
}

当这个生产者新发布一条信息时,我们立即就可以知道需要给哪些用户发布通知。这样做的缺点时,如果需要找到一个用户关注的用户列表,就必须查询整个用户集合。这样方式的优缺点与第一种方式的优缺点恰好相反。

同时,这两种方式都存在另一个问题:它们会使用户文档变得越来越大,改变也越来越频繁。通常,“following”和“followers”字段甚至不需要返回:查询粉丝列表有多频繁?如果用户比较频繁地关注某些人或者对一些人取消关注,也会导致大量的碎片。因此,最后的方案对数据进一步范式化,将订阅信息保存在单独的集合中,以避免这些缺点。进行这种成都的范式化可能有点儿过了,但是对于经常发生变化而且不需要与文档其他字段一起返回的字段,这非常有用。对“followers”字段做这种范式化使有意义的。

用一个集合来保存发布者和订阅者的关系,其中的文档结构可能如下所示:

{
 "_id": ObjectId("..."), //被关注者的"_id"
 "followers": [
 ObjectId("..."),
 ObjectId("..."),
 ObjectId("...")
 ]
}

这样可以使用户文档比较精简,但是需要额外的查询才能得到粉丝列表。由于“followers”数组的大小经常会发生变化,所以可以在这个集合上启用“usePowerOf2Sizes”,以保证users集合尽可能小。如果将followers集合保存在另一个数据库中,也可以在不过多影响users集合的前提下对其进行压缩。

应对威尔惠顿效应

不管使用什么样的策略,内嵌字段只能在子文档或者引用数量不是特别大的情况下有效发挥作用。对于比较有名的用户,可能会导致用于保存粉丝列表的文档溢出。对于这种情况的一种解决方案使在必要时使用“连续的”文档。例如:

> db.users.find({"username": "wil"})
{
 "_id": ObjectId("..."),
 "username": "wil",
 "email": "wil@example.com",
 "tbc": [
 ObjectId("123"), // just for example
 ObjectId("456") // same as above
 ],
 "followers": [
 ObjectId("..."),
 ObjectId("..."),
 ObjectId("..."),
 ...
 ]
}
{
 "_id": ObjectId("123"),
 "followers": [
 ObjectId("..."),
 ObjectId("..."),
 ObjectId("..."),
 ...
 ]
}
{
 "_id": ObjectId("456"),
 "followers": [
 ObjectId("..."),
 ObjectId("..."),
 ObjectId("..."),
 ...
 ]
}

对于这种情况,需要在应用程序中添加从“tbc”(to be continued)数组中取数据的相关逻辑。

说点什么

No silver bullet.

文档

MongoDB中的范式与反范式

MongoDB中的范式与反范式:本文摘录自《MongoDB权威指南》第八章,可以彻底回答以下两个问题: http://segmentfault.com/q/1010000000364944 http://segmentfault.com/q/1010000000364944 数据表示的方式有很多种,其中最重要的问题之一就是在多大程度上对数据进行范式化。范式化(n
推荐度:
标签: 中的 摘录 本文
  • 热门焦点

最新推荐

猜你喜欢

热门推荐

专题
Top