Как бы вы спроектировали хранилище данных AppEngine для социального сайта, такого как Twitter?

StackOverflow https://stackoverflow.com/questions/1630087

Вопрос

Мне интересно, как лучше всего разработать социальное приложение, участники которого совершают действия и следят за действиями других участников с помощью Google AppEngine.

Чтобы быть более конкретным, предположим, что у нас есть следующие сущности:

  • Пользователи у кого есть друзья
  • Деятельность которые представляют действия, совершаемые пользователями (скажем, у каждого есть строковое сообщение и ReferenceProperty для пользователя-владельца, или он может использовать родительскую ассоциацию через ключ appengine)

Самое сложное — следить за действиями вашего друга, что означает сбор последних действий всех ваших друзей.Обычно это будет соединение между таблицей «Действия» и списком ваших друзей, но это нежизнеспособная конструкция для appengine, поскольку нет соединений, имитирующих это, потребуется запустить N запросов (где N — количество друзей), а затем слияние в памяти — очень дорого и, вероятно, превысит срок запроса...)

В настоящее время я подумываю о реализации этого с использованием очередей входящих сообщений, где создание нового действия запускает фоновый процесс, который помещает ключ нового действия во «входящие» каждого следующего пользователя:

  • Получение «Все пользователи, которые следят за X» — это возможный запрос appengine.
  • Не очень дорогой пакетный ввод в новый объект «Входящие», который в основном хранит кортежи (Пользователь, Ключ активности).

Я буду рад услышать мысли об этом дизайне или альтернативные предложения и т. д.

Это было полезно?

Решение

Взгляни на Создание масштабируемых и сложных приложений на App Engine (PDF), увлекательный доклад Бретта Слаткина на Google I/O.Он обращается к проблеме создания масштабируемой службы обмена сообщениями, такой как Twitter.

Вот его решение используя свойство списка:

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)

Этот ключевой запрос находит индексы сообщений с получателем, равным указанному вами, без десериализации и сериализации списка получателей.Затем вы используете эти индексы, чтобы получить только те сообщения, которые вам нужны.

Вот неправильный путь сделать это:

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

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

Это неэффективно, поскольку запросы должны распаковывать все результаты, возвращаемые вашим запросом.Таким образом, если вы вернули 100 сообщений с 1000 пользователей в каждом списке получателей, вам придется десериализовать 100 000 (100 x 1000) значений свойств списка.Слишком дорого с точки зрения задержки хранилища данных и процессора.

Поначалу все это меня очень смутило, поэтому я написал краткое руководство по использованию свойства list.Наслаждаться :)

Другие советы

Я не знаю, является ли это лучший дизайн для социального приложения, но джайку был портирован на App Engine его первоначальным создателем, когда компания была приобретена Google, поэтому это должно быть разумно.

Смотрите раздел Актеры, тигры и медведи, о боже! в design_funument.txt.Сущности определены в общий/models.py и запросы находятся в общий/api.py.

Роберт, о предлагаемом вами решении:

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

Я думаю, что «тело» ndb.TextProperty нельзя использовать с проекциями, поскольку оно не индексируется.Проекции поддерживают только индексированные свойства.Правильным решением было бы сохранить две таблицы:Сообщение и Индекс сообщения.

Я думаю, что теперь эту проблему можно решить с помощью новых запросов проекции в 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])

Теперь вам не придется иметь дело с дорогостоящими затратами на десериализацию свойства списка.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top