2009-10-27 5 views
27

Ich frage mich, was wäre der beste Weg, um eine soziale Anwendung zu gestalten, bei der Mitglieder mit Google AppEngine Aktivitäten ausführen und den Aktivitäten anderer Mitglieder folgen.Wie würden Sie einen AppEngine-Datenspeicher für eine soziale Website wie Twitter entwerfen?

Um genauer sein lässt vermuten wir diese Entitäten haben:

  • Benutzer, die Freunde haben
  • Aktivitäten welche Aktionen gemacht werden von den Benutzern darstellen (können sagen, die jeweils eine Zeichenfolge Nachricht und eine Reference an seinen Besitzer Benutzer, oder es kann Elternvereinigung über appengine Schlüssel verwenden)

Der schwierige Teil folgt den Aktivitäten Ihres Freundes, Das bedeutet, dass Sie die neuesten Aktivitäten von all Ihren Freunden zusammenfassen. Normalerweise wäre das ein Join zwischen der Activities-Tabelle und Ihrer Freundesliste, aber das ist kein praktikabler Entwurf für appengine, da es keinen simulierenden Join gibt, der N-Abfragen auslösen muss (wobei N die Anzahl der Freunde ist) und dann im Speicher zusammengeführt werden - sehr teuer und wird wahrscheinlich Anfragetermin überschreiten ...)

Ich denke derzeit über die Implementierung dieser Posteingangswarteschlangen, wo Erstellung einer neuen Aktivität einen Hintergrundprozess ausgelöst wird, die den Schlüssel der neuen Aktivität in den "Posteingang "jeder folgenden Benutzer:

  • bekommen‚Alle Nutzer, die X folgen‘ist eine mögliche appengine Abfrage
  • Keine sehr teure Stapeleingabe in eine neue "Posteingang" -Entität, die grundsätzlich Tupel (Benutzer-, Aktivitätsschlüssel) speichert.

Ich werde gehört Gedanken auf diesem Entwurf oder alternative Vorschläge usw. glücklich sein

+1

Ich schaute auf das gleiche Problem und fand diese ausgezeichnete (!) Präsentation von der AppEngine, die sie bei Google I/O gegeben: http://www.scribd.com/doc/16952419/Building-Scalable-complex -apps-on-App-Engine Ich hoffe du findest es auch nützlich. –

Antwort

24

bei Building Scalable, Complex Apps on App Engine Werfen Sie einen Blick (pdf), eine faszinierende Rede gegeben bei Google I/O von Brett Slatkin. Er spricht das Problem an, einen skalierbaren Messaging-Dienst wie Twitter aufzubauen.

Hier ist seine Lösung eine Liste Eigenschaft mit:

class Message(db.Model): 
    sender = db.StringProperty() 
    body = db.TextProperty() 

class MessageIndex(db.Model): 
    #parent = a message 
    receivers = db.StringListProperty() 

indexes = MessageIndex.all(keys_only = True).filter('receivers = ', user_id) 
keys = [k.parent() for k in indexes) 
messages = db.get(keys) 

Dieser Schlüssel nur Abfrage findet die Nachrichten Indizes mit einem Empfänger gleich dem Sie ohne Deserialisieren angegeben und die Liste der Empfänger der Serialisierung. Dann verwenden Sie diese Indizes, um nur die gewünschten Nachrichten zu erfassen.

Hier der falsche Weg, es zu tun:

class Message(db.Model): 
    sender = db.StringProperty() 
    receivers = db.StringListProperty() 
    body = db.TextProperty() 

messages = Message.all().filter('receivers =', user_id) 

Dies ist ineffizient, da Abfragen alle Ergebnisse durch Ihre Frage zurück auszupacken haben. Wenn Sie also in jeder Empfängerliste 100 Nachrichten mit 1.000 Benutzern zurückgegeben haben, müssen Sie 100.000 (100 x 1000) Listeneigenschaftswerte deserialisieren. Viel zu teuer in Datenspeicher-Latenz und CPU.

Ich war ziemlich verwirrt von all dem zuerst, so schrieb ich eine short tutorial about using the list property.Viel Spaß :)

+0

Genau mein ursprüngliches Design. Aber was ich aus diesem Vortrag und aus der AppEngine-Dokumentation verstanden habe, ist, dass Listen nutzlos sind, wenn es um IN-Anfragen geht. Die Abfrage, die Sie erwähnten, löst mehrere Abfragen in Google System aus, die jeweils nach einem der Werte in den Listeneigenschaften filtern und dann das Ergebnis zusammenführen. Google begrenzt diese Art von Abfrage auf 30 gleichzeitige Abfragen, was bedeutet, dass es nur für eine Liste verwendet werden kann, die eine relativ kleine Anzahl von Schlüsseln (<30) enthält. Wenn es um Freunde geht, könnte diese Liste Zehn, wenn nicht Hunderte (oder Tausende?) Von Schlüsseln für Personen enthalten, denen Sie folgen. –

+0

BTW Ich fragte Sie die gleiche Frage bezüglich Listen in einer anderen StackOverflow Frage, die Sie gepostet :) –

+0

Ich glaube nicht, dass das richtig ist. Brett sagt, dass man auf 5000 indizierte Eigenschaften pro Entity beschränkt ist, wenn er über die Performance der Listeneigenschaften spricht (siehe 14:15 im Video). Ich denke, Sie sollten in der Lage sein, Tausende von Benutzern in einem Empfänger StringListProperty zu haben, während immer noch in der Lage, eine effiziente Abfrage durchzuführen. Ich bin mir nicht sicher, was die Zeile "Eine einzelne Abfrage mit! = Oder IN-Operatoren ist auf 30 Unterabfragen beschränkt" bedeutet, aber ich bin mir sicher, es hat keinen Einfluss darauf, was Sie hier machen wollen. – wings

7

Ich weiß nicht, ob es das ist beste Entwurf für eine soziale Anwendung, aber jaiku war ported to App Engine von seinem ursprünglichen Schöpfer, als das Unternehmen von Google übernommen wurde, so sollte es sinnvoll sein.

Siehe den Abschnitt Schauspieler und Tiger und Bären, Oh My! in design_funument.txt. Die Entitäten sind in common/models.py definiert und die Abfragen sind in common/api.py.

+0

Vielen Dank! Dieser Code ist eine großartige Referenz ... –

0

Ich denke, das kann jetzt mit den neuen Projection Queries in NDB gelöst werden.

class Message(ndb.Model): 
    sender = ndb.StringProperty() 
    receivers = ndb.StringProperty(repeated=True) 
    body = ndb.TextProperty() 

messages = Message.query(Message.receivers == user_id).fetch(projection=[Message.body]) 

Jetzt müssen Sie sich nicht mit dem teuer Kosten beschäftigen, die Liste Eigenschaft deserialisiert.

0

Robert, über Ihre vorgeschlagene Lösung:

messages = Message.query(Message.receivers == user_id).fetch(projection=[Message.body]) 

ich denke, der ndb.TextProperty „Körper“ kann nicht mit Projektionen verwendet werden, da nicht indiziert. Projektionen unterstützen nur indexierte Eigenschaften. Die gültige Lösung wäre, die 2 Tabellen zu pflegen: Message und MessageIndex.

Verwandte Themen