В продолжение моей статьи про шаблоны, хочу рассказать про реализацию кэширования в Django. Основной упор будет сделан на кэширование частей шаблона – этот вопрос был затронут тут и послужил причиной написания этих двух статей. В предыдущей статье я слишком увлекся описанием самих шаблонов, так что постараюсь исправиться в этой.
Что такое кэширование, и какие оно дает преимущества под нагрузкой, думаю никому объяснять не надо. (Вступительные слова можно прочитать в начале соответствующего раздела документации по Django, ссылки в конце статьи.) Что можно кэшировать?
Остановимся подробнее на реализации этого в Django.
Настройка механизма кэширования в Django очень проста. В файле настроек проекта нужно указать переменную
Поддерживаются следующие параметры кэша:
Синтаксис описан в документации, и повторять его нет смысла.
Тем, у кого есть свои сервера или возможность выделит пару гигабайт памяти под кэш, думаю, эта статья будет малоинтересна. Поэтому хранение в Memcached и локальной памяти я описывать не буду. Выбор между БД и ФС предоставлю читателю. Насколько быстрее тот или иной способ я не сравнивал. Для себя я выбрал ФС т.к. БД и так нагружена.
Кроме того, для удобства разработки, поддерживается еще «dummy» хранилище, которое на самом деле ничего не хранит.
Думаю, будет уместно описать способ безболезненного объединения этих двух версий.
Основным файлом конфигурации проекта Django является
Функция platform.node возвращает имя компьютера, и я сравниваю его с именем своего сервера. Наверное, можно придумать более универсальное решение, но мне достаточно этого.
Таким образом, различающиеся настройки я выношу в эти файлы.
В нашем случае переменная
Для кэширования всего сайта достаточно добавить
Кэшируются все страницы без параметров.
Подробнее в документации.
Для кэширования страницы используется функция
Также ее можно использовать в качестве декоратора:
Параметр – время обновления кэша в секундах.
Для кэширования данных кэш Django предоставляет ожидаемые функции:
Предыдущая часть статьи большей частью дублировала документацию Django – специально для того чтобы показать незнакомым с джангой, но знакомым с хабром, что кэширование в Django не требует от программиста никаких усилий.
Кэширование частей шаблона мне кажется наиболее удобным для использования. Для динамичных сайтов (читай «в общем случае») кэшировать всю страницу нельзя. А в шаблоне мы кэшируем только нужный нам участок.
Для начала в шаблоне необходимо подключить библиотеку тегов кэширования.
Тэг cache принимает два обязательных параметра: время обновления кэша (в секундах) и имя кэшируемого фрагмента.
Кроме того можно передать сколько угодно дополнительных параметров для хранения нескольких версий кэша. Примерами могут быть разные версии кэша в зависимости от имени пользователя или от номера страницы при постраничной разбивке данных внутри блока.
Попробую показать наглядно, что при этом происходит, и какие дает нам преимущества.
Возьмем пример из предыдущей статьи. Для шаблона
Что тут можно кэшировать? В первую очередь нужно кэшировать редко изменяющиеся данные, доставаемые из базы.
Если мы даем пользователю возможность изменить информацию в «подвале» сайта стоит ее кэшировать – изменяется она очень редко.
Посмотрев внимательнее на примеры шаблонов в предыдущей статье можно увидеть, что вторая колонка (sidebar) не меняется для всех страниц блога. Поскольку для ее заполнения выполняется довольно много запросов (у меня – список тегов, количество статей для каждого тега, список последних статей) ее стоит кэшировать.
Список статей тоже делает довольно много запросов: нужно выбрать статьи в зависимости от страницы и для каждой статьи выбрать соответствующие ей теги.
Получим следующую картину:
Что происходит при запросе этой страницы?
При построении дерева для каждого тега вызываются конструирующие его функции, предоставляющие данные для отрисовки. Именно в этот момент подготавливаются запросы в базу. Непосредственно сами запросы происходят при обращении к данным по причине их ленивой природы в Django. Затем парсер шаблонов обегает дерево и для каждого блока вызывает функцию отрисовки. В случае кэширующего тега вложенные в него теги будут сконструированы и отрисованы только если кэш еще не создан или устарел.
Если не использовать теги (лениться «плодить» теги для одного запроса в базу), то все данные необходимо предоставить самому шаблону. Да, по причине ленивой природы запросов в Django, разница не очень существенная. Однако получается не очень логично – контроллер вроде как отработал, а результатами его работы никто не воспользовался. Даже можно писать кучу питоновского кода в expr-тегах внутри самого шаблона – кэшу все равно. Однако подобные «нарушения» архитектуры намного сложнее поддерживать. А уж если это писал другой человек… работая в веб-студии и поддерживая кучу сайтов, я в этом убедился.
Теперь комментарий к статье, которая послужила поводом написания всего этого.
dmmd, фактически Вы предлагаете зачаточный вариант того, что реализовано в Django: теги и ленивые запросы в базу. Суть остается той же, однако архитектура Django кажется мне намного более стройной, очевидной и, соответственно легче поддерживаемой.
Ссылки на официальную документацию:
PS: это как всегда, перепечатка с моего блога
Что такое кэширование, и какие оно дает преимущества под нагрузкой, думаю никому объяснять не надо. (Вступительные слова можно прочитать в начале соответствующего раздела документации по Django, ссылки в конце статьи.) Что можно кэшировать?
- весь сайт;
- конкретную страницу;
- участок шаблона;
- какие-то данные.
Остановимся подробнее на реализации этого в Django.
Подключение и настройка
Настройка механизма кэширования в Django очень проста. В файле настроек проекта нужно указать переменную
CACHE_BACKEND
содержащую параметры хранения кэша: где, как долго и сколько. Поддерживаются следующие типы хранилищ:- Memcached (в том числе на другой машине);
- база данных;
- файловая система;
- локальная память.
Поддерживаются следующие параметры кэша:
timeout
– время обновления кеша, в секундах;max_entries
– максимальное количество записей в кэше;cull_frequency
– процент старых записей, который удаляется по достижениюmax_entries
.
Синтаксис описан в документации, и повторять его нет смысла.
Тем, у кого есть свои сервера или возможность выделит пару гигабайт памяти под кэш, думаю, эта статья будет малоинтересна. Поэтому хранение в Memcached и локальной памяти я описывать не буду. Выбор между БД и ФС предоставлю читателю. Насколько быстрее тот или иной способ я не сравнивал. Для себя я выбрал ФС т.к. БД и так нагружена.
Кроме того, для удобства разработки, поддерживается еще «dummy» хранилище, которое на самом деле ничего не хранит.
Отступление: production & development версии
Думаю, будет уместно описать способ безболезненного объединения этих двух версий.
Основным файлом конфигурации проекта Django является
settings.py
. Я добавил еще два файла: settings_dev.py
и settings_pub.py
, а в settings.py
написал:import platform DEV_MODE=(platform.node()!='dpp.su') if DEV_MODE: DEBUG = True from settings_dev import * else: DEBUG = False from settings_pub import *
Функция platform.node возвращает имя компьютера, и я сравниваю его с именем своего сервера. Наверное, можно придумать более универсальное решение, но мне достаточно этого.
Таким образом, различающиеся настройки я выношу в эти файлы.
В нашем случае переменная
CACHE_BACKEND
в settings_dev.py
будет равна 'dummy:///'
, а в settings_pub.py
— 'file:///path/to/cache/'
Кэширование всего сайта
Для кэширования всего сайта достаточно добавить
'django.middleware.cache.CacheMiddleware'
в список MIDDLEWARE_CLASSES
и переменную CACHE_MIDDLEWARE_SECONDS
.Кэшируются все страницы без параметров.
Подробнее в документации.
Кэширование страницы (view)
Для кэширования страницы используется функция
cache_page
:from django.views.decorators.cache import cache_page def cache_this(request): ... cache_this = cache_page(cache_this, 60 * 15)
Также ее можно использовать в качестве декоратора:
@cache_page(60 * 15) def cache_this(request): ...
Параметр – время обновления кэша в секундах.
Кэширование данных
Для кэширования данных кэш Django предоставляет ожидаемые функции:
set, get, delete
и пару дополнительных. Настройки кэша берутся из переменной CACHE_BACKEND
. Детали в документации.Предыдущая часть статьи большей частью дублировала документацию Django – специально для того чтобы показать незнакомым с джангой, но знакомым с хабром, что кэширование в Django не требует от программиста никаких усилий.
Кэширование фрагмента шаблона
Кэширование частей шаблона мне кажется наиболее удобным для использования. Для динамичных сайтов (читай «в общем случае») кэшировать всю страницу нельзя. А в шаблоне мы кэшируем только нужный нам участок.
Для начала в шаблоне необходимо подключить библиотеку тегов кэширования.
{% load cache %}
Тэг cache принимает два обязательных параметра: время обновления кэша (в секундах) и имя кэшируемого фрагмента.
{% cache 500 sidebar %} .. sidebar .. {% endcache %}
Кроме того можно передать сколько угодно дополнительных параметров для хранения нескольких версий кэша. Примерами могут быть разные версии кэша в зависимости от имени пользователя или от номера страницы при постраничной разбивке данных внутри блока.
{% cache 500 sidebar request.user.username %} .. sidebar for logged in user .. {% endcache %} {% cache 500 sidebar page %} .. content varies by page parameter .. {% endcache %}
Попробую показать наглядно, что при этом происходит, и какие дает нам преимущества.
Возьмем пример из предыдущей статьи. Для шаблона
blog/tag_index.htm
будет построено следующее дерево (соответствие цветов см. в предыдущей статье):Что тут можно кэшировать? В первую очередь нужно кэшировать редко изменяющиеся данные, доставаемые из базы.
Если мы даем пользователю возможность изменить информацию в «подвале» сайта стоит ее кэшировать – изменяется она очень редко.
{% block footer %} {% cache 5000 foot %} {% block copyright %} {% endblock %} {% endcache %} {% endblock %}
Посмотрев внимательнее на примеры шаблонов в предыдущей статье можно увидеть, что вторая колонка (sidebar) не меняется для всех страниц блога. Поскольку для ее заполнения выполняется довольно много запросов (у меня – список тегов, количество статей для каждого тега, список последних статей) ее стоит кэшировать.
{% block sidebar %} {% cache 500 blog_sidebar %} {% block tags %} {% endblock %} {% block recent %} {% endblock %} {% endcache %} {% endblock %}
Список статей тоже делает довольно много запросов: нужно выбрать статьи в зависимости от страницы и для каждой статьи выбрать соответствующие ей теги.
{% block content %} {{ tag.title }} {{ tag.text }} {% cache 500 article_list tag.id page %} {{ block.super }} {% endcache %} {% endblock %}
Получим следующую картину:
Что происходит при запросе этой страницы?
При построении дерева для каждого тега вызываются конструирующие его функции, предоставляющие данные для отрисовки. Именно в этот момент подготавливаются запросы в базу. Непосредственно сами запросы происходят при обращении к данным по причине их ленивой природы в Django. Затем парсер шаблонов обегает дерево и для каждого блока вызывает функцию отрисовки. В случае кэширующего тега вложенные в него теги будут сконструированы и отрисованы только если кэш еще не создан или устарел.
Если не использовать теги (лениться «плодить» теги для одного запроса в базу), то все данные необходимо предоставить самому шаблону. Да, по причине ленивой природы запросов в Django, разница не очень существенная. Однако получается не очень логично – контроллер вроде как отработал, а результатами его работы никто не воспользовался. Даже можно писать кучу питоновского кода в expr-тегах внутри самого шаблона – кэшу все равно. Однако подобные «нарушения» архитектуры намного сложнее поддерживать. А уж если это писал другой человек… работая в веб-студии и поддерживая кучу сайтов, я в этом убедился.
Теперь комментарий к статье, которая послужила поводом написания всего этого.
dmmd, фактически Вы предлагаете зачаточный вариант того, что реализовано в Django: теги и ленивые запросы в базу. Суть остается той же, однако архитектура Django кажется мне намного более стройной, очевидной и, соответственно легче поддерживаемой.
Ссылки на официальную документацию:
- djangodocs: Django’s cache framework
- djangobook: Django’s cache framework
- djangobook (перевод): Кэширование
PS: это как всегда, перепечатка с моего блога