我正在为一个拥有约500,000用户的Twitter风格社交网络开发一个概念验证应用程序。我不确定如何最好地设计'架构'
我应该嵌入用户的订阅还是拥有单独的“订阅”集合并使用数据库引用?如果我嵌入,我仍然需要执行查询以获取所有用户的关注者。 e.g。
鉴于以下用户:
{
"username" : "alan",
"photo": "123.jpg",
"subscriptions" : [
{"username" : "john", "status" : "accepted"},
{"username" : "paul", "status" : "pending"}
]
}
找到所有阿兰的订阅者,我必须运行这样的东西:
db.users.find({'subscriptions.username' : 'alan'});
从性能的角度来看,是否比拥有单独的订阅集合更糟或更好?
另外,当显示订阅/订阅者列表时,我目前遇到n + 1问题,因为订阅文档告诉我目标用户的用户名,但不是我可能需要的其他属性,例如个人资料照片。是否有针对此类情况的推荐做法?
感谢 艾伦
答案 0 :(得分:12)
首先,你应该知道你将使用MongoDB和任何其他NoSQL数据库获得权衡(但要意识到我是它的粉丝)。如果您试图完全规范化数据,那么您就犯了一个大错误。即使在关系数据库中,您的应用程序越大,您的数据被非规范化的次数就越多(请参阅Hot Potato的this post)。我一次又一次地看到了这一点。你不应该疯了,弄得一团糟,但不要担心在两个地方重复信息。 NoSQL的一个主要观点(在我看来)是你的模式进入你的代码而不仅仅是数据库。
现在,为了回答你的问题,我认为你最初的策略是我会做的。 MongoDB可以将索引放在数组元素上,这样如果您正在寻找用户拥有多少友谊,这将使事情变得更快。但实际上,真正确定的唯一方法是运行某种测试程序,生成一个充满名称和关系的数据库。
您可以使用Python或Perl或任何您喜欢的内容编写一些输入,并使用名称文件生成一些关系。查看Census website,其中包含姓氏列表。下载文件dist.all.last
并编写一些程序,如:
#! /usr/bin/env python
import random as rand
f = open('dist.all.last')
names = []
for line in f:
names.append(line.split()[0])
rels = {}
for name in names:
numOfFriends = rand.randint(0, 1000)
rels[name] = []
for i in range(numOfFriends):
newFriend = rand.choice(names)
if newFriend != name: #cannot be friends with yourself
rels[name].append(newFriend)
# take relationships (i.e. rels) and write them to MongoDB
另外,作为一般说明,您的字段名称似乎有点长。请记住,使用该集合中的每个文档重复字段名,因为您不能依赖于任何其他文档中的一个字段。为了节省空间,一般策略是使用较短的字段名称,如“unam”而不是“username”,但这是一件小事。请参阅these two帖子中的好建议。
修改强>
实际上,在更多地思考你的问题时,我会再提出一个建议:将订阅类型分解到不同的字段以使索引更有效。例如,而不是:
{
"username" : "alan",
"photo": "123.jpg",
"subscriptions" : [
{"username" : "john", "status" : "accepted"},
{"username" : "paul", "status" : "pending"}
]
}
如上所述,我会这样做:
{
"username" : "alan",
"photo": "123.jpg",
"acc_subs" : [ "john" ],
"pnd_subs" : [ "paul" ]
}
这样你就可以为每种类型的订阅设置一个索引,从而进行诸如“很多人让保罗等待处理?”之类的查询。和“有多少人订阅保罗?”无论哪种方式超快。 Mongo对数组值的索引确实是史诗般的胜利。
答案 1 :(得分:2)
@Alan B :我认为你完全得到了MongoDB。我同意 @daveslab 版本的数据,但您可能也想添加“关注者”。
{
"username" : "alan",
"photo": "123.jpg",
"acc_subs" : [ "john" ],
"pnd_subs" : [ "paul" ]
"acc_fol" : [ "mike", "ray" ],
"pnd_fol" : [ "judy" ]
}
是的,这是重复的信息。这取决于“业务层”,以确保在两个位置正确更新此数据。不幸的是,Mongo没有交易,幸运的是,你有$ addToSet操作,所以你很安全。