我正在为一个拥有约500,000用户的推特式社交网络开发一个概念证明应用程序.我不确定如何最好地设计'架构'
我应该嵌入用户的订阅还是拥有单独的"订阅"集合并使用数据库引用?如果我嵌入,我仍然需要执行查询以获取所有用户的关注者.例如
鉴于以下用户:
{ "username" : "alan", "photo": "123.jpg", "subscriptions" : [ {"username" : "john", "status" : "accepted"}, {"username" : "paul", "status" : "pending"} ] }
为了找到所有阿兰的订阅者,我必须运行这样的事情:
db.users.find({'subscriptions.username' : 'alan'});
从性能的角度来看,是否比拥有单独的订阅集合更糟或更好?
此外,当显示订阅/订阅者列表时,我目前遇到n + 1问题,因为订阅文档告诉我目标用户的用户名,但不是我可能需要的其他属性,例如个人资料照片.是否有针对此类情况的推荐做法?
谢谢艾伦
首先,你应该知道你将通过MongoDB和任何其他NoSQL数据库获得的权衡(但我意识到我是它的粉丝).如果您试图完全规范化数据,那么您就犯了一个大错误.即使在关系数据库中,您的应用程序越大,您的数据被非规范化的次数就越多(请参阅Hot Potato的这篇文章).我一次又一次地看到了这一点.你不应该疯了,弄得一团糟,但不要担心在两个地方重复信息.NoSQL的一个主要观点(在我看来)是你的架构进入你的代码而不仅仅是数据库.
现在,为了回答你的问题,我认为你最初的策略是我会做的.MongoDB可以将索引放在数组元素上,这样如果您正在寻找用户拥有多少友谊,这将使事情变得更快.但实际上,真正确定的唯一方法是运行某种测试程序,生成一个充满名称和关系的数据库.
您可以使用Python或Perl或任何您喜欢的内容编写一些输入脚本,并使用名称文件生成一些关系.查看人口普查网站,其中包含姓氏列表.下载文件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",但这是一件小事.看看这 两篇文章中的好建议.
编辑:
实际上,在进一步思考你的问题时,我会再提出一个建议:将订阅类型分解到不同的字段以使索引更有效.例如,而不是:
{ "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" ] }
因此,您可以为每种类型的订阅设置一个索引,从而进行"Hoy很多人让Paul等待处理"这样的查询.和"有多少人订阅保罗?" 无论哪种方式超快.Mongo对数组值的索引确实是史诗般的胜利.