Evgenii Legotckoi
Evgenii Legotckoi3. November 2019 04:03

EVILEG-KERN. Caching-Eigenschaften von Modellobjekten mit model_cached_property

Um die Website zu beschleunigen, können Sie neben der Optimierung von Datenbankabfragen auch Caching verwenden.

Django erlaubt Caching:

  • individuelle Ansicht , sowohl klassenbasierte Ansicht als auch normale Ansicht -Funktionen
  • ganze Vorlagen oder Teile dieser Vorlagen
  • Abfragesatz
  • sowie Eigenschaften von Modellobjekten mit cached_property

Ich war an der Möglichkeit interessiert, einzelne Eigenschaften von Modellobjekten für umfangreiche Berechnungen oder umfangreiche Datenbankabfragen zwischenzuspeichern.
Der Decorator cached_property hat eine solche Funktionalität, aber das Minus für mich war, dass das Caching nur für die Lebensdauer des Objekts erfolgte.
Während ich das Caching für einen längeren Zeitraum benötige als die Existenz des Objekts, wenn die Seite angefordert wird. Außerdem musste ich Eigenschaften abhängig von den Eingabeargumenten zwischenspeichern. Dieser Site-Decorator speichert die Anzahl der Vorlieben und Abneigungen sowie Informationen darüber, ob dem aktuellen Benutzer ein bestimmtes Inhaltselement gefallen hat.

So wurde der Decorator model_cached_property geschrieben


model_cached_property

Dieser Decorator verwendet redis als Caching-Mechanismus, da das Ungültigmachen des Caches möglicherweise das Löschen der Gruppe von Schlüsseln erfordert, die zu dieser Eigenschaft gehören. Da die Eigenschaft für verschiedene Benutzer auf unterschiedliche Weise zwischengespeichert werden kann.

Installation von EVILEG-CORE

pip install evileg-core

Außerdem zieht evileg_core alle für dieses Paket erforderlichen Abhängigkeiten ein. Einschließlich der Bibliothek django-redis , die verwendet wird, um mit redis zu arbeiten.

Wenn Sie redis noch nicht verwenden, müssen Sie es installieren.

sudo apt install redis-server

settings.py

evileg_core zu installierten Anwendungen hinzufügen

INSTALLED_APPS = [
    ...
    'evileg_core',
]

Einrichten eines Caching-Backends

CACHES = {
    "default": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379/1",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
        }
    }
}

Mit model_cached_property

Normales Caching

from evileg_core.cache.decorators import model_cached_property

class Article(models.Model):

    @model_cached_property
    def comments_count(self):
        return self.comments.count()

Dieser Decorator speichert die Kommentaranzahl des Artikels für 60 Sekunden. Bei der nächsten Anforderung einer Artikelseite erfolgt zunächst die Anforderung an den Cache für das jeweilige Artikelobjekt, und erst wenn der Cache abgelaufen ist, erfolgt erneut eine Abfrage an die Datenbank.

Caching-Timeout festlegen

Wenn Sie eine bestimmte Cache-Zeit festlegen möchten, können Sie das timeout -Argument mit einem Decorator verwenden.

class Article(models.Model):

    @model_cached_property(timeout=3000)
    def comments_count(self):
        return self.comments.count()

Einstellung der globalen Cache-Dauer

Sie können auch eine globale Cache-Zeit für alle Decorators in settings.py in Sekunden festlegen.

MODEL_CACHED_PROPERTY_TIMEOUT = 300000

Eigenschaften mit Argumenten verwenden

Und jetzt das Interessanteste. Zwischenspeichern von Eigenschaften mit Argumenten, sodass Sie das Ergebnis unter Verwendung von Informationen zu den Eingabeargumenten der Datenmodelleigenschaft zwischenspeichern können.

Dies ist sowohl ein Vorteil dieses Dekorateurs als auch ein Nachteil. Tatsache ist, dass das Caching korrekt ist, es ist notwendig, dass die Eingabeargumente eindeutig sind. Wenn die Eingabeargumente beispielsweise temporäre nicht eindeutige Objekte wie AnonymousUser sind, funktioniert das Caching nicht.

Je nach Benutzer kann jedoch ein Decorator zum Caching verwendet werden. So könnte es aussehen.

class Article(models.Model):

    @model_cached_property
    def __user_in_bookmarks(self, user):
        return self.bookmarks.filter(user=user).exists()

    def user_in_bookmarks(self, user):
        return self.__user_in_bookmarks(user) if user.is_authenticated else False

Beachten Sie, dass es eine Prüfung auf user.is_authenticated gibt, da das Zwischenspeichern eines nicht authentifizierten Benutzers nicht korrekt funktioniert, aber für einen authentifizierten Benutzer wird es korrekt funktionieren, da der authentifizierte Benutzer ein eindeutiges Objekt ist.

Cache-Invalidierung

Für den Fall, dass der Cache vor Ablauf seiner Lebensdauer irrelevant geworden ist, können Sie die Funktion invalidate_model_cached_property verwenden

from evileg_core.cache.utils import invalidate_model_cached_property

class Article(models.Model):

    def invalidate_cache(self):
        invalidate_model_cached_property(self, self.comments_count)

Um den Cache korrekt zu invalidieren, müssen Sie das Objekt übergeben, dessen Cache Sie löschen möchten, sowie die Methode dieses Objekts.

Es kann genauso aussehen.

article = get_object_or_404(Article, pk=12)
invalidate_model_cached_property(article, article.comments_count)

Fazit

Daher kann model_cached_property verwendet werden

  • Caching-Eigenschaften von Modellobjekten für lange Zeit, längere Objektlebensdauer beim Anfordern einer Seite
  • Caching-Eigenschaften von Modellobjekten in Abhängigkeit von Eingabeargumenten

Es gibt Einschränkungen

  • Dieser Decorator kann nur in Modellen verwendet werden, d. h. Klassen, die von models.Model geerbt wurden
  • Das Zwischenspeichern von Modelleigenschaften in Abhängigkeit von Argumenten sollte nur für eindeutige Eingabeargumente erfolgen, da das Zwischenspeichern sonst nicht korrekt ist
Рекомендуємо хостинг TIMEWEB
Рекомендуємо хостинг TIMEWEB
Stabiles Hosting des sozialen Netzwerks EVILEG. Wir empfehlen VDS-Hosting für Django-Projekte.

Magst du es? In sozialen Netzwerken teilen!

МК
  • 6. November 2019 02:25

а functools.lru_cache в данном случае не поможет?

Evgenii Legotckoi
  • 6. November 2019 03:16
  • (bearbeitet)

Добрый день.

Думаю, что не совсем. Технически здесь решается задача кэширования не последних вызовов функции, как в lru_cache, а кэширование свойств для ряда объектов модели данных. К тому же cache_clear() будет полностью удалять весь кэш, тогда как у меня предусмотрен более специфический функционал для инвалидации части ключей, которые перестали быть актуальны.

Давайте поясню на примере.

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

Так вот, если использовать lru_cache, то он будет кэшировать лишь n-последних вызовов метода, то есть придётся отслеживать количество контента и вручную поправлять количество последних кэширований, если решать эту задачу в лоб. А в случае очистки кэша, при вызове страниц будут заново кэшироваться все свойства для всех объектов ForumTopic. Получается ситуация, если пользователь лайкнул один единственный пост, то кэш будет очищаться для всех объектов, чего я не хочу.

model_cached_property же работает немного иначе. Он кэширует информацию о модели данных, а именно о таблице в базе данных, а также об id объекта, а потом информацию об имени свойства и входных аргументах. В результате происходит уникальное кжширование для каждого отдельного объекта.

Когда вы вызываете функцию invalidate_model_cached_property, то кэш очищается только для конкретного объекта базы данных.

Таким образом, когда пользователь лайкнет один какой-то объект ForumTopic , то кэш будет очищен только для этого ForumTopic . И при следующем запросе список объектов ForumTopic будет выполняться запрос о лайках и дислайках только для одного единственного объекта. Плюс я не беспокоюсь о том, что мне нужно думать, какой максимальный размер кэша нужно делать.

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

Но если я что-то не учёл и у вас есть, что дополнить, то с удовольствием выслушаю вашу точку зрения, возможно, что это поможет доработать функциональность model_cached_property

NSProject
  • 27. Mai 2022 06:27
  • (bearbeitet)

Всё конечно супер классно. Интересует лишь один вопрос.

NSProject
  • 28. Mai 2022 10:20

Здравствуйте. В общем меня интересует такой вопрос. Я пробовал это на Like , Dislike. Как я понимаю если не перевалидировать кеш то ничего не изменится на странице. Вернётся значение из кэша? Отсюда второй вопрос как и где прописть эту перевалидацию? Ибо если я пишу её через модель как сказано в статье. То ничего не происходит. По этому интересует то как и где перевалидировать кеш.

Evgenii Legotckoi
  • 30. Mai 2022 10:25

Да, если не вызывать invalidate_cache , то ничего не произойдёт.
Место вызова инвалидации обычно индивидуально. Но я стараюсь вешать его на сигналы сохранения и удаления объектов.

В случае с Like Dislike , которые используют GenericForeignKey удобнее всего поступить следующим образом.

Добавить метод invalidate_cache , который будет вызывать метод инвалидации content_object

class LikeDislike(...):

    ...

    def invalidate_cache(self):
        self.content_object.invalidate_like_dislike_cache()

А потом навешать инвалидацию на сигналы

# -*- coding: utf-8 -*-

from django.db.models.signals import post_save, pre_delete

from .models import LikeDislike

def cache_invalidate_activity(sender, instance, **kwargs):
    instance.invalidate_cache()

post_save.connect(cache_invalidate_activity, sender=LikeDislike)
pre_delete.connect(cache_invalidate_activity, sender=LikeDislike)

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

NSProject
  • 2. Juni 2022 09:33

Спасибо за пояснения. Я ток щас догнал как это всё работает. Просто с сигналами не работал никогда. Но вот теперь стало понятно.

Kommentare

Nur autorisierte Benutzer können Kommentare posten.
Bitte Anmelden oder Registrieren
Letzte Kommentare
ИМ
Игорь Максимов5. Oktober 2024 07:51
Django – Lektion 064. So schreiben Sie eine Python-Markdown-Erweiterung Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
d
dblas55. Juli 2024 11:02
QML - Lektion 016. SQLite-Datenbank und das Arbeiten damit in QML Qt Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
k
kmssr8. Februar 2024 18:43
Qt Linux - Lektion 001. Autorun Qt-Anwendung unter Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
Qt WinAPI - Lektion 007. Arbeiten mit ICMP-Ping in Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
EVA
EVA25. Dezember 2023 10:30
Boost - statisches Verknüpfen im CMake-Projekt unter Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
Jetzt im Forum diskutieren
J
JacobFib17. Oktober 2024 03:27
добавить qlineseries в функции Пользователь может получить любые разъяснения по интересующим вопросам, касающимся обработки его персональных данных, обратившись к Оператору с помощью электронной почты https://topdecorpro.ru…
JW
Jhon Wick1. Oktober 2024 15:52
Indian Food Restaurant In Columbus OH| Layla’s Kitchen Indian Restaurant If you're looking for a truly authentic https://www.laylaskitchenrestaurantohio.com/ , Layla’s Kitchen Indian Restaurant is your go-to destination. Located at 6152 Cleveland Ave, Colu…
КГ
Кирилл Гусарев27. September 2024 09:09
Не запускается программа на Qt: точка входа в процедуру не найдена в библиотеке DLL Написал программу на C++ Qt в Qt Creator, сбилдил Release с помощью MinGW 64-bit, бинарнику напихал dll-ки с помощью windeployqt.exe. При попытке запуска моей сбилженной программы выдаёт три оши…
F
Fynjy22. Juli 2024 04:15
при создании qml проекта Kits есть но недоступны для выбора Поставил Qt Creator 11.0.2. Qt 6.4.3 При создании проекта Qml не могу выбрать Kits, они все недоступны, хотя настроены и при создании обычного Qt Widget приложения их можно выбрать. В чем может …

Folgen Sie uns in sozialen Netzwerken