2010-05-15 5 views
17

Je travaille sur une application de preuve de concept pour un réseau social de style twitter avec environ 500k utilisateurs. Je ne suis pas sûr de la meilleure façon de concevoir le «schéma»design 'schema' pour un réseau social

dois-je intégrer les abonnements d'un utilisateur ou avoir une collection distincte d'abonnements et utiliser des références db? Si je l'intègre, je dois encore effectuer une requête pour obtenir tous les abonnés d'un utilisateur. par exemple.

Compte tenu de l'utilisateur suivant:

{ 
"username" : "alan", 
"photo": "123.jpg", 
"subscriptions" : [ 
    {"username" : "john", "status" : "accepted"}, 
    {"username" : "paul", "status" : "pending"} 
    ] 
} 

pour trouver tous les abonnés de alan, je dois courir quelque chose comme ceci:

db.users.find({'subscriptions.username' : 'alan'}); 

d'un point de vue de la performance, est que tout pire ou mieux que d'avoir une collection d'abonnements séparés? J'ai également des problèmes avec n + 1 parce que le document d'abonnement me dit le nom d'utilisateur de l'utilisateur cible mais pas d'autres attributs dont j'ai besoin comme la photo de profil. Y a-t-il des pratiques recommandées pour de telles situations?

grâce Alan

Répondre

11

Tout d'abord, vous devez connaître les compromis que vous allez obtenir avec MongoDB et toute autre base de données NoSQL (mais se rendre compte que je suis un fan de celui-ci). Si vous essayez de normaliser complètement vos données, vous faites une grosse erreur. Même dans les bases de données relationnelles, plus votre application est importante, plus vos données sont dénormalisées (voir this post par Hot Potato). J'ai vu cela encore et encore. Vous ne devriez pas devenir fou et faire un énorme gâchis, mais ne vous inquiétez pas de répéter l'information à deux endroits. L'un des points majeurs (à mon avis) de NoSQL est que votre schéma se déplace dans votre code et pas seulement dans la base de données. Maintenant, pour répondre à votre question, je pense que votre stratégie initiale est ce que je ferais. MongoDB peut placer des index sur des éléments qui sont des tableaux, ce qui rendra les choses beaucoup plus rapides si vous recherchez le nombre d'amitiés d'un utilisateur. Mais en réalité, la seule façon d'être vraiment sûr est d'exécuter un programme de test qui génère une base de données pleine de noms et de relations.

Vous pouvez écrire des scripts en Python ou Perl ou utiliser un fichier de noms pour générer des relations. Découvrez le Census website, qui a une liste de noms de famille. Télécharger le fichier dist.all.last et écrire certains programmes comme:

#! /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 

En outre, comme une note générale, vos fieldnames semblent sorte de long. Rappelez-vous que les noms de champs sont répétés avec tous les documents dans cette collection, car vous ne pouvez pas compter sur un champ figurant dans un autre document. Pour économiser de l'espace, une stratégie générale consiste à utiliser des noms de champ plus courts comme "unam" au lieu de "nom d'utilisateur", mais c'est une petite chose. Voir le bon conseil dans thesetwo messages.

EDIT:

En fait, en réfléchissant votre problème un peu plus, je voudrais faire une autre suggestion: briser les types d'abonnement dans différents domaines pour rendre les indices plus efficaces.Par exemple, au lieu de:

{ 
"username" : "alan", 
"photo": "123.jpg", 
"subscriptions" : [ 
    {"username" : "john", "status" : "accepted"}, 
    {"username" : "paul", "status" : "pending"} 
    ] 
} 

Comme vous dit plus haut, je ferais ceci:

{ 
"username" : "alan", 
"photo": "123.jpg", 
"acc_subs" : [ "john" ], 
"pnd_subs" : [ "paul" ] 
} 

Alors que vous pourriez avoir un index pour chaque type d'abonnement, rendant ainsi des requêtes telles que « Hoy beaucoup les gens ont Paul en attente? " et "Combien de personnes souscrivent à Paul?" super rapide de toute façon. L'indexation de Mongo sur les valeurs array'd est vraiment une victoire épique.

+2

bon message, +1, mais je ne suis pas d'accord sur les noms courts. Faites-les aussi longtemps que nécessaire pour ne rien expliquer à un autre développeur. Puis profile/optimise au besoin. Si les noms sont un problème de taille important à mesure que vous mettez à l'échelle puis refactoriser. – Lee

2

@Alan B: Je pense que vous obtenez totalement MongoDB. Je suis d'accord avec @daveslab version des données, mais vous voudrez probablement ajouter "followers" aussi.

{ 
"username" : "alan", 
"photo": "123.jpg", 
"acc_subs" : [ "john" ], 
"pnd_subs" : [ "paul" ] 
"acc_fol" : [ "mike", "ray" ], 
"pnd_fol" : [ "judy" ] 
} 

Oui c'est une information en double. C'est à la «couche de gestion» de s'assurer que ces données sont correctement mises à jour dans les deux endroits. Malheureusement, il n'y a pas de transactions dans Mongo, heureusement, vous avez l'opération $ addToSet, donc vous êtes assez sûr.

Questions connexes