2009-10-27 7 views
27

Zastanawiam się, jaki byłby najlepszy sposób zaprojektowania aplikacji społecznościowej, w której członkowie podejmują działania i śledzą działania innych członków za pomocą Google AppEngine.Jak zaprojektowałbyś magazyn danych AppEngine dla serwisu społecznościowego, takiego jak Twitter?

Aby być bardziej konkretny pozwala zakładać mamy te podmioty:

  • Użytkownicy, którzy mają przyjaciół
  • Inne które stanowią działania podejmowane przez użytkowników (powiedzmy, że każdy ma wiadomość ciąg i ReferenceProperty do właściciela, lub może korzystać z powiązania rodzicielskiego za pośrednictwem klucza aplikacji)

Najtrudniejsze jest śledzenie aktywności twojego przyjaciela, co oznacza agregowanie najnowszych działań ze wszystkimi Twoimi przyjaciółmi. Normalnie byłoby to połączenie między tabelą działań i twoją listą przyjaciół, ale to nie jest opłacalny projekt na appengine, ponieważ nie ma symulacji łączenia, będzie to wymagało odpalania zapytań N (gdzie N jest liczbą znajomych), a następnie scalania w pamięci - bardzo drogie i prawdopodobnie przekroczy termin składania wniosków ...)

Mam zamiar wprowadzić tę kolejkę do skrzynki odbiorczej, gdzie utworzenie nowego działania uruchomi proces w tle, który umieści klucz nowego działania w "skrzynce odbiorczej" "każdego następnego użytkownika:

  • Getting«Wszyscy użytkownicy, którzy podążają X»jest możliwe kwerendy AppEngine
  • Niezbyt kosztowne wejście wsadowe do nowej jednostki "Odebrane", która zasadniczo przechowuje krotki (klucz użytkownika, klucz aktywności).

będę szczęśliwy słyszał myśli o tym wzorem lub alternatywnych sugestie itp

+1

Szukałem tego samego problemu i znalazłem tę znakomitą (!) Prezentację z AppEngine, którą podali w Google I/O: http://www.scribd.com/doc/16952419/Building-scalable-complex -apps-on-app-Engine Mam nadzieję, że również ci się to przyda. –

Odpowiedz

24

Spójrz na Building Scalable, Complex Apps on App Engine (pdf), fascynujący wykład na Google I/O Brett Slatkinem. Zajmuje się problemem budowy skalowalnej usługi przesyłania wiadomości, takiej jak Twitter.

Oto jego rozwiązanie używając własności list:

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) 

Ten klucz tylko zapytania znajdzie indeksy wiadomość z odbiornika równej jednym określonym bez deserializacji i szeregowania listy odbiorców. Następnie użyj tych indeksów, aby pobrać tylko te wiadomości, które chcesz.

Oto niewłaściwy sposób to zrobić:

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

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

To jest nieefektywne, ponieważ zapytania trzeba rozpakować wszystkich wyników zwracanych przez zapytania. Jeśli więc zwrócisz 100 wiadomości z 1000 użytkowników na każdej liście odbiorców, musisz przekształcić z postaci 100 000 (100 x 1000) wartości właściwości listy. O wiele za drogi w opóźnieniach magazynu danych i procesorze.

Początkowo byłem bardzo zdezorientowany, więc napisałem short tutorial about using the list property.Enjoy :)

+0

Dokładnie mój początkowy projekt. Ale zrozumiałem z tej rozmowy i z dokumentacji AppEngine jest to, że listy są całkiem bezużyteczne, gdy przychodzi do zapytań IN. Wspomniane zapytanie wywoła kilka zapytań w systemie google, z których każde będzie filtrowane według jednej z wartości we właściwościach listy, a następnie scalane z wynikiem. Google przerywa tego rodzaju zapytanie na 30 równoczesnych zapytań, co oznacza, że ​​można go używać tylko dla listy, która będzie zawierała stosunkowo małą liczbę kluczy (<30). Jeśli chodzi o przyjaciół, lista ta może zawierać dziesiątki, jeśli nie setki (lub tysiące?) Kluczy dla osób, które obserwujesz. –

+0

btw Zadałem ci to samo pytanie dotyczące list w innym pytaniu StackOverflow, które napisałeś :) –

+0

Nie sądzę, że to prawda. Brett mówi, że jesteś ograniczony do 5000 indeksowanych nieruchomości na jednostkę, gdy mówi o wydajności właściwości listy (zobacz 14:15 w wideo). Myślę, że powinieneś być w stanie mieć tysiące użytkowników w odbiornikach StringListProperty, a jednocześnie być w stanie wykonać wydajne zapytanie. Nie jestem pewien, co oznacza wiersz "Pojedyncze zapytanie zawierające operatorów! = Lub IN jest ograniczone do 30 sub-zapytań", ale pozytywnie nie ma wpływu na to, co chcesz tutaj zrobić. – wings

7

Nie wiem, czy jest to najlepiej projekt dla aplikacji społecznej, ale jaiku był ported to App Engine przez nią oryginalnym twórcą kiedy firma została przejęta przez Google, więc powinno być uzasadnione.

Zobacz dział Aktorzy i tygrysy i niedźwiedzie, Oh My! w design_funument.txt. Podmioty są zdefiniowane w common/models.py, a zapytania są w common/api.py.

+0

Wielkie dzięki! ten kod to świetne odniesienie ... –

0

Myślę, że teraz można to rozwiązać za pomocą nowych zapytań projekcyjnych w NDB.

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]) 

Teraz nie musisz zajmować się kosztownymi kosztami deserializacji właściwości listy.

0

Robert, o swojej proponowanego rozwiązania:

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

Myślę, że „ciało” ndb.TextProperty nie może być używany z prognozami, ponieważ nie jest indeksowana. Prognozy obsługują tylko właściwości indeksowane. Prawidłowym rozwiązaniem byłoby zachowanie dwóch tabel: Message i MessageIndex.

Powiązane problemy