2010-04-17 13 views
2

J'ai un modèle de données simple qui comprendGoogle App Engine: Mauvaises performances avec JDO + Datastore

UTILISATEURS: stocker des informations de base (clé, nom, numéro de téléphone, etc.)

RELATIONS: décrire, par exemple une amitié entre deux utilisateurs (fournissant un relationship_type + deux clés utilisateur)

COMMENTAIRES: posté par les utilisateurs (clés, le texte de commentaire, user_id)

j'obtenir des performances très pauvres, par exemple, si je tente de imprime les prénoms de tous les amis d'un utilisateur. Dire que l'utilisateur a 500 amis: Je peux aller chercher la liste des amis user_ids très facilement dans une seule requête. Mais ensuite, pour sortir les prénoms, je dois faire 500 allers et retours vers le Datastore, chacun d'entre eux semblant prendre de l'ordre de 30 ms. Si c'était SQL, je ferais juste un JOIN et obtenir la réponse rapidement. Je comprends qu'il existe des installations rudimentaires pour réaliser des jointures bidirectionnelles à travers des relations non-possédées dans une implémentation détendue de JDO (comme décrit à http://gae-java-persistence.blogspot.com) mais elles semblent expérimentales et non standard (par exemple mon code ne fonctionnera pas dans toute autre mise en œuvre de JDO). Pire encore, que se passe-t-il si je veux sortir tous les commentaires postés par les amis d'un utilisateur. Ensuite, je dois obtenir de User -> Relation -> Comments, c'est-à-dire une jointure à trois voies, qui n'est même pas supportée expérimentalement. L'overhead de 500 va-et-vient pour obtenir une liste d'amis + 500 autres voyages pour voir s'il y a des commentaires des amis d'un utilisateur est déjà suffisant pour pousser le temps d'exécution> 30 secondes.

Comment les gens gèrent-ils ces problèmes dans les applications JDO soutenues par la banque de données réelles? (Ou ils le font?)

Est-ce que quelqu'un a réussi à extraire des performances satisfaisantes de JDO/Datastore dans ce genre de situation (très commune)?

-Bosh

Répondre

3

tout d'abord, pour les objets qui sont souvent accessibles (comme les utilisateurs), je compte sur le memcache. Cela devrait accélérer votre application un peu.

Si vous devez accéder à la banque de données, la méthode correcte doit être getObjectsById(). Malheureusement, il ressemble à GAE doesn't optimize this call. Cependant, une requête sur les touches est optimized pour aller chercher tous les objets en un seul voyage à la datastore, c'est ce que vous devez utiliser:

List myFriendKeys = fetchFriendKeys(); 
Query query = pm.newQuery(User.class, ":p.contains(key)"); 
query.execute(myFriendKeys); 

Vous pouvez également compter sur l'API de bas niveau get() qui acceptent plusieurs clés , ou fais comme moi et utilise objectify.

Une approche totalement différente consisterait à utiliser un filtre d'égalité sur une propriété de liste. Cela correspondra si n'importe quel article dans la liste correspond. Donc, si vous avez une propriété de liste friendOf dans votre entité utilisateur, vous pouvez émettre une seule requête friendOf == theUser. Vous pouvez vérifier ceci: http://www.scribd.com/doc/16952419/Building-scalable-complex-apps-on-App-Engine

+0

À moins que AppEngine ne prenne en charge les appels à contains() sur les champs Collection, il semble que vous ayez la meilleure solution. – Gunslinger47

0

Facebook a 28 téraoctets de mémoire cache ... Cependant, faire 500 voyages vers memcached n'est pas très bon marché non plus. Il ne peut pas être utilisé pour stocker un gazillion de petits objets. "Dénominalation" est la clé. Ces applications n'ont pas besoin de prendre en charge les requêtes ad-hoc. Calculez et stockez les résultats directement pour les quelques requêtes prises en charge.Dans votre cas, vous avez probablement 1 type de requête - les données de retour de ceci, cela et les autres qui devraient être affichées sur une page utilisateur.

Vous pouvez précalculer cette grosse boule de dégâts, donc plus tard une requête basée sur userId peut tout récupérer. Lorsque l'utilisateur A fait un commentaire à l'utilisateur B, vous récupérez la grosse boule de désordre de l'utilisateur B, y insérez le commentaire de l'utilisateur et l'enregistrez.

Bien sûr, cette approche présente de nombreux problèmes. Pour les sociétés Internet géantes, elles n'ont probablement pas le choix, les moteurs de requêtes génériques ne les coupent pas. Mais pour les autres? Ne seriez-vous pas plus heureux si vous pouviez utiliser le bon vieux SGBDR?

1

Malheureusement Phillipe la suggestion

Query query = pm.newQuery(User.class, ":p.contains(key)"); 

est seulement optimisé pour faire une seule requête lors de la recherche par clé primaire. En passant dans une liste de dix valeurs-clés non primaire, par exemple, donne la trace suivante alt text http://img293.imageshack.us/img293/7227/slowquery.png

Je voudrais pouvoir en vrac récupérer les commentaires, par exemple, de tous les amis de l'utilisateur. Si je stocke une liste sur chaque utilisateur, cette liste ne peut pas être plus longue que 1000 éléments (s'il s'agit d'une propriété indexée de l'utilisateur) comme décrit à: http://code.google.com/appengine/docs/java/datastore/overview.html.

Semble de plus en plus comme si j'utilise le mauvais ensemble d'outils ici.

-B

2

Vous avez pour minimiser les lectures de DB. Cela doit être un énorme objectif pour tout projet GAE - tout ce qui vous coûtera. Pour ce faire, pré-calculer autant que vous le pouvez, en particulier des informations lues. Pour résoudre le problème de la lecture de 500 noms d'amis, sachez que vous modifierez probablement la liste d'amis beaucoup moins que de la lire, donc à chaque changement, stockez tous les noms dans une structure que vous pouvez lire en un seul clic.

Si vous ne pouvez absolument pas, alors vous devez ajuster chaque cas à la main, par ex. utiliser l'API de bas niveau pour faire un batch.

Aussi, plutôt optimiser pour la vitesse et non la taille des données. Utilisez des structures supplémentaires en tant qu'index, sauvegardez les objets de plusieurs façons afin de pouvoir les lire le plus rapidement possible. Les données sont bon marché, le temps CPU n'est pas.

0

La limite de propriété indexée est maintenant porté à 5000.

Cependant, vous pouvez aller encore plus élevé que celui en utilisant la méthode décrite dans http://www.scribd.com/doc/16952419/Building-scalable-complex-apps-on-App-Engine
Fondamentalement ont juste un groupe d'entités enfant pour l'utilisateur appelé UserFriends, ainsi diviser la grande liste et augmenter la limite à n * 5000, où n est le nombre d'entités UserFriends.

Questions connexes