Как бы вы создали хранилище данных AppEngine для социального сайта, такого как Twitter?
Мне интересно, как лучше всего разработать социальное приложение, в котором участники будут выполнять действия и выполнять другие действия с помощью Google AppEngine.
Чтобы быть более конкретным, предположим, что мы имеем эти сущности:
- Пользователи, у кого есть друзья
- Действия, которые представляют действия, сделанные пользователями (например, каждое из них имеет строковое сообщение и ReferenceProperty для своего владельца или может использовать родительскую ассоциацию с помощью ключа appengine)
Жесткая часть следит за действиями ваших друзей, что означает объединение последних действий со всех ваших друзей.
Обычно это будет объединение между таблицей "Деятельность" и списком друзей, но это не жизнеспособный дизайн приложения, так как нет имитации соединения, для этого требуется запуск N запросов (где N - количество друзей), а затем слияние в памяти - очень дорогой и, вероятно, превысит срок подачи запроса...)
В настоящее время я собираюсь реализовать это, используя очереди входящих сообщений, когда создание нового Activity будет запускать фоновый процесс, который поместит новый ключ активности в "входящие" для каждого следующего пользователя:
- Получение "Все пользователи, которые следуют за X" - это возможный запрос appengine
- Не очень дорогой пакетный ввод в новый объект "Входящие", который в основном хранит кортежи (Пользователь, активность).
Я буду рад услышать мысль об этом дизайне или альтернативных предложениях и т.д.
Ответы
Ответ 1
Взгляните на Создание масштабируемых, сложных приложений в 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). Слишком дорого стоит время ожидания хранилища и процессор.
Сначала я был полностью смущен всем этим, поэтому я написал короткий учебник
Ответ 2
Я не знаю, является ли это лучшим дизайном для социального приложения, но jaiku был портирован в App Engine оригинальным создателем, когда компания была приобретена Google, поэтому она должна быть разумной.
Смотрите раздел Актеры и тигры и медведи, Oh My! в design_funument.txt. Объекты определены в common/models.py, и запросы находятся в common/api.py.
Ответ 3
Я думаю, что теперь это можно решить с помощью новых проекционных запросов в 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])
Теперь вам не нужно иметь дело с дорогостоящей стоимостью десериализации свойства списка.
Ответ 4
Роберт, о вашем предлагаемом решении:
messages = Message.query(Message.receivers == user_id).fetch(projection=[Message.body])
Я думаю, что "тело" ndb.TextProperty не может использоваться с проекциями, потому что не индексируется. Проекции поддерживают только индексированные свойства.
Допустимым решением было бы поддерживать 2 таблицы: Message и MessageIndex.