Сайтты жеделдету үшін дерекқор сұрауларын оңтайландырудан басқа, кэштеуді пайдалануға болады.
Django кэштеуге мүмкіндік береді:
- жеке көрініс , Класқа негізделген көрініс және тұрақты көру функциялары
- тұтас үлгілер немесе осы үлгілердің бөліктері
- Сұраулар жинағы
- сонымен қатар cached_property көмегімен үлгі нысандарының қасиеттері
Мені ауыр есептеулер немесе ауыр дерекқор сұраулары үшін үлгі нысандарының жеке қасиеттерін кэштеу мүмкіндігі қызықтырды.
cached_property
декораторының осындай функционалдығы бар, бірақ мен үшін минус кэштеу объектінің қызмет ету мерзімі ішінде ғана орын алды.
Ал маған бет сұралған кезде объектінің бар болуынан ұзақ уақыт бойы кэштеу қажет. Сондай-ақ кіріс аргументтеріне байланысты қасиеттерді кэштеу керек болды. Бұл сайт безендірушісі ұнатулар мен ұнатпаулар санын, сондай-ақ ағымдағы пайдаланушыға белгілі бір мазмұн элементін ұнатқан-ұнамағаны туралы ақпаратты кэштейді.
model_cached_property декораторы осылай жазылған
үлгінің_кэштелген_сипаты
Бұл декоратор өзінің кэштеу механизмі ретінде redis пайдаланады, себебі кэшті жарамсыз деп тану үшін осы сипатқа жататын кілттер тобын жою қажет болуы мүмкін. Өйткені сипат әртүрлі пайдаланушылар үшін әртүрлі жолдармен кэштелуі мүмкін.
EVILEG-CORE орнату
pip install evileg-core
Сондай-ақ, evileg_core осы пакетке қажетті барлық тәуелділіктерді тартады. Соның ішінде redis -мен жұмыс істеу үшін пайдаланылатын django-redis кітапханасы.
Егер сіз әлі redis қолданбасаңыз, оны орнатуыңыз керек.
sudo apt install redis-server
settings.py
Орнатылған қолданбаларға evileg_core қосыңыз
INSTALLED_APPS = [ ... 'evileg_core', ]
Кэштеу серверін орнату
CACHES = { "default": { "BACKEND": "django_redis.cache.RedisCache", "LOCATION": "redis://127.0.0.1:6379/1", "OPTIONS": { "CLIENT_CLASS": "django_redis.client.DefaultClient", } } }
Үлгі_кэштелген_сипатты пайдалану
Қалыпты кэштеу
from evileg_core.cache.decorators import model_cached_property class Article(models.Model): @model_cached_property def comments_count(self): return self.comments.count()
Бұл декоратор мақаланың пікірлер санын 60 секунд ішінде кэштейді. Келесі жолы мақала беті сұралғанда, сұрау алдымен белгілі бір мақала нысанының кэшіне жасалады және кэштің мерзімі өткен жағдайда ғана сұрау дерекқорға қайта жасалады.
Кэштеу уақытын орнату
Белгілі бір кэш уақытын орнатқыңыз келсе, декоратормен тайм-аут аргументін пайдалануға болады.
class Article(models.Model): @model_cached_property(timeout=3000) def comments_count(self): return self.comments.count()
Жаһандық кэш ұзақтығы параметрі
Сондай-ақ settings.py ішіндегі барлық декораторлар үшін жаһандық кэш уақытын секундтарда орнатуға болады.
MODEL_CACHED_PROPERTY_TIMEOUT = 300000
Аргументтермен сипаттарды пайдалану
Ал енді ең қызығы. Аргументтермен сипаттарды кэштеу, осылайша деректер үлгісі сипатының кіріс аргументтері туралы ақпаратты пайдаланып нәтижені кэштей аласыз.
Бұл декоратордың артықшылығы да, кемшілігі де. Өйткені, кэштеу дұрыс, кіріс аргументтері бірегей болуы керек. Мысалы, егер енгізу аргументтері AnonymousUser сияқты уақытша бірегей емес нысандар болса, кэштеу жұмыс істемейді.
Дегенмен, пайдаланушыға байланысты кэштеу үшін декораторды пайдалануға болады. Мынадай көрінуі мүмкін.
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
user.is_authenticated үшін тексеру бар екенін ескеріңіз, себебі аутентификацияланбаған пайдаланушыны кэштеу дұрыс жұмыс істемейді, бірақ түпнұсқалығы расталған пайдаланушы үшін ол дұрыс жұмыс істейді, себебі аутентификацияланған пайдаланушы бірегей нысан болып табылады.
Кэштің жарамсыздығы
Кэш оның қызмет ету мерзімі аяқталғанға дейін маңызды емес болып қалса, invalidate_model_cached_property функциясын пайдалануға болады.
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)
Кэшті дұрыс жарамсыз деп тану үшін кэшті тазалағыңыз келетін нысанды, сондай-ақ осы нысанның әдісін өтуіңіз керек.
Бұл бірдей көрінуі мүмкін.
article = get_object_or_404(Article, pk=12) invalidate_model_cached_property(article, article.comments_count)
Қорытынды
Сондықтан model_cached_property үшін қолдануға болады
- ұзақ уақыт бойы үлгі нысандарының кэштеу қасиеттері, бетті сұрау кезінде нысанның қызмет ету мерзімі ұзағырақ
- енгізу аргументтеріне байланысты модель объектілерінің кэштеу қасиеттері
Шектеулер бар
- бұл декораторды тек үлгілерде, яғни models.Model -дан мұраланған сыныптарда пайдалануға болады.
- аргументтерге байланысты үлгі қасиеттерін кэштеу тек бірегей енгізу аргументтері үшін орындалуы керек, әйтпесе кэштеу дұрыс емес болады.
а functools.lru_cache в данном случае не поможет?
Добрый день.
Думаю, что не совсем. Технически здесь решается задача кэширования не последних вызовов функции, как в lru_cache, а кэширование свойств для ряда объектов модели данных. К тому же cache_clear() будет полностью удалять весь кэш, тогда как у меня предусмотрен более специфический функционал для инвалидации части ключей, которые перестали быть актуальны.
Давайте поясню на примере.
На сайте есть модель данных ForumTopic , у которой есть лайки и дислайки, а также они подсвечиваются, если пользователь выбрал лайк или дислайк. Вся эта информация у меня кэшируется, поскольку для лайков и дислайков используются GenericForeignKey и запросы к базе данных посылаются отдельно, что получается несколько накладно для страниц со списком тем на форуме. Поэтому я предпочитаю кэшировать эти данные и при срабатывание некоторых событий делать инвалидацию кэша.
Так вот, если использовать lru_cache, то он будет кэшировать лишь n-последних вызовов метода, то есть придётся отслеживать количество контента и вручную поправлять количество последних кэширований, если решать эту задачу в лоб. А в случае очистки кэша, при вызове страниц будут заново кэшироваться все свойства для всех объектов ForumTopic. Получается ситуация, если пользователь лайкнул один единственный пост, то кэш будет очищаться для всех объектов, чего я не хочу.
model_cached_property же работает немного иначе. Он кэширует информацию о модели данных, а именно о таблице в базе данных, а также об id объекта, а потом информацию об имени свойства и входных аргументах. В результате происходит уникальное кжширование для каждого отдельного объекта.
Когда вы вызываете функцию invalidate_model_cached_property, то кэш очищается только для конкретного объекта базы данных.
Таким образом, когда пользователь лайкнет один какой-то объект ForumTopic , то кэш будет очищен только для этого ForumTopic . И при следующем запросе список объектов ForumTopic будет выполняться запрос о лайках и дислайках только для одного единственного объекта. Плюс я не беспокоюсь о том, что мне нужно думать, какой максимальный размер кэша нужно делать.
Конечно, вопрос расхода памяти на кэширование является довольно важным и если появится бутылочное горлышко, то я буду его решать. Но на данный момент я поставил у себя длительность кэширования подобных активностей, как лайки, на целый месяц и на данный момент ещё не заметил проблем.
Но если я что-то не учёл и у вас есть, что дополнить, то с удовольствием выслушаю вашу точку зрения, возможно, что это поможет доработать функциональность model_cached_property
Всё конечно супер классно. Интересует лишь один вопрос.
Здравствуйте. В общем меня интересует такой вопрос. Я пробовал это на Like , Dislike. Как я понимаю если не перевалидировать кеш то ничего не изменится на странице. Вернётся значение из кэша? Отсюда второй вопрос как и где прописть эту перевалидацию? Ибо если я пишу её через модель как сказано в статье. То ничего не происходит. По этому интересует то как и где перевалидировать кеш.
Да, если не вызывать invalidate_cache , то ничего не произойдёт.
Место вызова инвалидации обычно индивидуально. Но я стараюсь вешать его на сигналы сохранения и удаления объектов.
В случае с Like Dislike , которые используют GenericForeignKey удобнее всего поступить следующим образом.
Добавить метод invalidate_cache , который будет вызывать метод инвалидации content_object
А потом навешать инвалидацию на сигналы
В таком случае инвалидация будет действовать на лету, главное, чтобы во всех моделях были, к которым привязываются лайки и дизлайки, были определены соответсвующие методы инвалидации.
Спасибо за пояснения. Я ток щас догнал как это всё работает. Просто с сигналами не работал никогда. Но вот теперь стало понятно.