GAE datastore - лучшая практика, когда записей больше, чем чтений - PullRequest
3 голосов
/ 09 апреля 2011

Я пытаюсь попрактиковаться с хранилищем данных GAE, чтобы получить представление о механизмах запросов и выставления счетов.

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

Я создал супер простое приложение:

  • существует две веб-страницы - одна для выбора ссылоки одним просмотром выбранных ссылок
  • каждый пользователь может выбрать добавление URL-ссылок в свой «канал ссылок»
  • . Пользователь может выбрать столько ссылок, сколько он хочет, в любое время.
  • на другой веб-странице, я хочу показать пользователю самые последние 10 ссылок, которые он выбрал.
  • каждый пользователь имеет свою собственную веб-страницу "ссылок".
  • на каждой "ссылке"Я хочу сохранить и показать некоторые метаданные - например: сама ссылка URL;когда это было выбрано;сколько раз оно уже появилось в ленте;и т. д.

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

Вопрос 1: Я могу придумать (как минимум) два варианта, как обрабатывать данныедля этого приложения:

Вариант A: - сохранить объект на пользователя с данными пользователя, регистрацией и т. д. - сохранить другой объект на пользователя, который содержит его последние 10 выбранных ссылок, которые будут отображатьсяна веб-страницу пользователя после того, как он ее запросит

Опция B: - сохранить объект на ссылку URL - это означает, что все URL всех пользователей будут сохранены как один и тот же объект - сохранить объект наданные пользователя (такие же, как в варианте A), но добавьте ссылку на URL пользователя в большую таблицу URL

Какой метод будет лучше?

Вопрос 2: Если я хочу посчитатьобщее количество URL-адресов, выбранных до сегодняшнего дня, или ежедневное количество URL-адресов, которые выбрал пользователь, или любой другой счет - должен ли я использовать его с моими инструментами SDK или я должен вставить счетчики в сущности, которые я описал выше?(Я хочу максимально уменьшить количество записей в хранилище данных)

РЕДАКТИРОВАТЬ (чтобы ответить на комментарий @ Elad): Предположим, я хочу сохранить только 10 последних URL-адресов для пользователей.от остальных я хочу избавиться (чтобы не перенаселять мою БД ненужными данными).

РЕДАКТИРОВАТЬ 2: после добавления кода Итак, я сделал попытку с помощью следующего кода (пробуя сначала метод Элада):

Вот мой класс:

class UserChannel(db.Model):
currentUser = db.UserProperty()
userCount = db.IntegerProperty(default=0)
currentList = db.StringListProperty() #holds the last 20-30 urls

Затем я сериализовал URL-адрес и метаданные в строки JSON, которые пользователь отправляет с первой страницы.Вот как обрабатывается POST:

def post(self):
    user = users.get_current_user()
    if user:  
        logging messages for debugging
        self.response.headers['Content-Type'] = 'text/html'
        #self.response.out.write('<p>the user_id is: %s</p>' % user.user_id())            
        updating the new item that user adds
        current_user = UserChannel.get_by_key_name(user.nickname())
        dataJson = self.request.get('dataJson')
        #self.response.out.write('<p>the dataJson is: %s</p>' % dataJson) 
        current_user.currentPlaylist.append(dataJson)
        sizePlaylist= len(current_user.currentPlaylist)
        self.response.out.write('<p>size of currentplaylist is: %s</p>' % sizePlaylist)
        #whenever the list gets to 30 I cut it to be 20 long
        if sizePlaylist > 30:
            for i in range (0,9):
                current_user.currentPlaylist.pop(i)
        current_user.userCount +=1
        current_user.put()
        Updater().send_update(dataJson) 
    else:
        self.response.headers['Content-Type'] = 'text/html'
        self.response.out.write('user_not_logged_in')

где Updater - это мой метод обновления через Channel-API веб-страницы с помощью канала.

Теперь все работает, я вижу каждого пользователя.имеет ListProperty с 20-30 ссылками (когда он достигает 30, я сократил его до 20 с помощью pop ()), но!цены довольно высоки ... каждый POST, такой как здесь, занимает ~ 200 мс, 121 cpu_ms, cpm_usd = 0,003588.Это очень дорого, учитывая все, что я делаю, это сохраняю строку в списке ... Думаю, проблема может заключаться в том, что сущность становится большой с большим ListProperty?

Ответы [ 2 ]

1 голос
/ 09 апреля 2011

Во-первых, вы вправе беспокоиться о большом количестве записей в хранилище данных GAE - по моему опыту, они очень дороги по сравнению с чтением. Например, мое приложение, которое ничего не делало, кроме вставки записей в одну таблицу моделей, исчерпало свободную квоту с несколькими десятками тысяч записей в день. Таким образом, обработка записей эффективно переводит непосредственно в вашу прибыль.

Первый вопрос

Я не буду хранить ссылки как отдельные объекты. Хранилище данных не является СУБД, поэтому стандартные методы нормализации не обязательно применяются. Для каждого объекта User используйте ListProperty для хранения самых последних URL-адресов вместе с их метаданными (вы можете сериализовать все в строку).

  • Это эффективно для записи, так как вы обновляете только одну запись - нет обновлений для всех записей ссылок, когда пользователь добавляет ссылки. Помните, что для хранения скользящего списка (FIFO) со ссылочными URL-адресами, хранящимися как отдельные модели, каждый новый URL-адрес означает два действия записи - вставка нового URL-адреса и удаление для удаления самого старого.
  • Это также эффективно для чтения, так как одно чтение пользовательской записи дает вам все данные, необходимые для визуализации канала пользователя.
  • С точки зрения хранилища, общее количество URL-адресов в мире намного превышает ваше количество пользователей (даже если вы станете следующим Facebook), также как и разница URL-адресов, выбранных вашими пользователями, поэтому, вероятно, среднее значение У URL будет один пользователь - никакого реального выигрыша в нормализации данных в стиле СУРБД.

Еще одна идея оптимизации: если ваши пользователи обычно добавляют несколько ссылок за короткий промежуток времени, вы можете попытаться написать их оптом, а не отдельно. Используйте memcache для хранения вновь добавленных пользовательских URL-адресов и Очередь задач для периодической записи этих временных данных в постоянное хранилище данных. Я не уверен, какова стоимость ресурсов при использовании Задач - вам придется проверить. Вот хорошая статья для чтения по теме.

Второй вопрос

Используйте счетчики. Просто имейте в виду, что они не являются тривиальными в распределенной среде, так что читайте - есть много статей GAE, рецептов и сообщений в блогах на эту тему - просто google appengine counters . Здесь также использование memcache должно быть хорошим вариантом для уменьшения общего числа записей в хранилище данных.

1 голос
/ 09 апреля 2011

Ответ 1

Хранить ссылки как отдельные объекты. Также сохраняйте сущность на пользователя с помощью ListProperty, имеющего ключи к последним 20 ссылкам. Когда пользователь выбирает больше ссылок, вы просто обновляете список свойств ключей. ListProperty поддерживает порядок, поэтому вам не нужно беспокоиться о хронологическом порядке выбранных ссылок, если вы следуете порядку вставки FIFO.

Если вы хотите показать выбранные пользователем ссылки (стр. 2), вы можете сделать один раз (ключи), чтобы получить все пользовательские ссылки за один вызов.

Ответ 2

Обязательно сохраняйте счетчики, так как число объектов увеличивается, сложность подсчета записей будет продолжать увеличиваться, но с счетчиками производительность останется прежней.

...