Высоко масштабируемые теги в Google App Engine (Python)
У меня много (например) постов, помеченных одним или несколькими тегами. Сообщение может быть создано или удалено, а также пользователь может сделать запрос поиска для одного или нескольких тегов (в сочетании с логическим И). Первая идея, которая пришла мне в голову, была простая модель
class Post(db.Model):
#blahblah
tags = db.StringListProperty()
Реализация операций создания и удаления очевидна. Поиск более сложный. Для поиска N тегов он будет выполнять N GQL-запросов, таких как "SELECT * FROM Post WHERE tags =:1", и объединит результаты с помощью курсоров, и он будет иметь ужасную производительность.
Вторая идея - разделить теги в разных сущностях.
class Post(db.Model):
#blahblah
tags = db.ListProperty(db.Key) # For fast access
class Tag(db.Model):
name = db.StringProperty(name="key")
posts = db.ListProperty(db.Key) # List of posts that marked with tag
Он берет теги из db по ключу (намного быстрее, чем GQL) и объединяет их в памяти, я думаю, что эта реализация имеет лучшую производительность, чем первая, но очень часто используемые теги могут превышать максимальный размер, который допускается для одного объекта хранилища данных, И есть еще одна проблема: хранилище данных может изменить один единственный объект только ~1/ сек, поэтому для часто используемых тегов у нас также есть узкое место с задержкой изменения.
Какие-либо предложения?
2 ответа
Для дальнейшего опроса Ника. Если это логическое И с использованием нескольких тегов в запросе. Использовать теги = тег1 и теги = тег2 ... установить членство в одном запросе - одна из ярких функций хранилища данных. Вы можете достичь своего результата одним запросом.
http://code.google.com/appengine/docs/python/datastore/queriesandindexes.html
Вероятно, возможное решение состоит в том, чтобы взять ваш второй пример и изменить его таким образом, чтобы обеспечить эффективные запросы на больших наборах. На ум приходит один способ - использовать несколько объектов базы данных для одного тега и сгруппировать их так, чтобы вам редко приходилось получать больше нескольких групп. Если порядок сортировки по умолчанию (хорошо, давайте просто назовем его единственно разрешенным) по дате публикации, то заполните группы тегов в этом порядке.
class Tag(db.Model):
name = db.StringProperty(name="key")
posts = db.ListProperty(db.Key) # List of posts that marked with tag
firstpost = db.DateTimeProperty()
При добавлении или удалении тегов в группу проверьте, сколько сообщений в этой группе. Если добавляемый вами пост приведет к тому, что пост содержит более, скажем, 100 сообщений, разбейте его на две группы тегов. Если вы удаляете сообщение, чтобы в группе было менее 50 сообщений, украдите несколько сообщений из предыдущей или следующей группы. Если в одной из смежных групп также есть 50 сообщений, просто объедините их. При выводе сообщений по тегу (в порядке пост-даты) вам нужно всего лишь несколько групп.
Это на самом деле не решает проблему тегов с высоким спросом.
Думая об этом, было бы хорошо для вставок быть немного более умозрительным. Получить последние записи группы тегов, объединить их и разместить новую группу тегов. Задержка в транзакциях может на самом деле не быть реальной проблемой.