Нужно ли в модели NDB ядра приложений явно кэшировать ссылки на связанные модели, чтобы минимизировать затраты на запросы и оптимизировать производительность?


Документация App Engine по кэшированию NDB указывает, что кэширование включено по умолчанию:

NDB автоматически кэширует данные, которые он записывает или читает (если приложение не настроит его на это).

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

Пользовательская модель (имеет много комментарии):

class User(ndb.Model):
    name                    = ndb.StringProperty(required=True)
    email                   = ndb.StringProperty(required=True)

    def comments(self, limit=25):
        return UserComment.query(UserComment.user_key == self.key) 
                          .order(-UserComment.created_at) 
                          .fetch(limit)

Модель комментариев (каждый комментарий принадлежит пользователю):

class UserComment(ndb.Model):
    user_key                = ndb.KeyProperty(required=True)
    text                    = ndb.StringProperty(required=True)
    created_at              = ndb.DateTimeProperty(auto_now_add=True)

    @property
    def user(self):
        return self.user_key.get()

И шаблон, в котором отображается комментарий, содержащий две ссылки на comment.user:

<div class="comment">
  <div class="body">
    {{ comment.text }}
  </div>
  <div class="footer">
    by {{ comment.user.name }} ({{ comment.user.email }})
  </div>
</div>

Является ли это нормальным паттерном? Будет ли каждая ссылка на comment.user.name и comment.user.email нести отдельную стоимость запроса или можно доверять автоматическому кэшу NDB, чтобы избежать или минимизировать это?

Аналогичным образом, с помощью метода User.comments Можно ли доверять автоматическому кэшированию, минимизируя затраты? Или желательно добавить код что явно использует memcache?

2 2

2 ответа:

Кэширование NDB охватывает сами сущности. Из упомянутого вами документа:

NDB автоматически кэширует данные, которые он записывает или читает (если только приложение настраивает его не на). Чтение из кэша происходит быстрее, чем чтение из хранилища данных.

Это означает, что по умолчанию вам не нужно беспокоиться о ручной обработке кэширования для прямого поиска ключей / свойств, таких как comment.user.name и comment.user.email, ndb позаботится об этом.

Однако запросы-это совсем другая история - невозможно узнать, остаются ли данные, возвращенные запросом, действительным ответом на тот же запрос, повторенный позже, - возможно, за это время были созданы дополнительные данные. Кэширование результатов запроса - это самое первое использование memcache, упомянутое в документации:

Одним из способов использования кэша памяти является ускорение обычных запросов к хранилищам данных. Если многие запросы делают один и тот же запрос с одинаковыми параметрами, и изменения результатов не должны появляться на веб-сайте право кроме того, приложение может кэшировать результаты в memcache. Последующий запросы могут проверять memcache и выполнять только запрос хранилища данных если результаты отсутствуют или истекли. Данные сеанса, предпочтения пользователя, и любые другие запросы, выполняемые на большинстве страниц сайта, хороши кандидаты на кэширование.
Другими словами, вы должны попытаться вручную кэшировать такие вещи, как User.comments, которые полагаются на запросы для их возвращаемого значения.

Каждый вызов UserComment.пользователь() выполнит get из хранилища данных. Но, второй звонок сам.пользовательский ключ.get () из того же ключа вернет кэшированную сущность.

Но все равно... это плохой дизайн, потому что вы выполняете отдельные вызовы RPC к хранилищу данных вместо получения всех пользовательских сущностей сразу с get_multi().