Django как обновить кэш
Django 1.2 представил новый загрузчик шаблонов, который хранит данные в кеше ( django.template.loaders.cached.Loader ).
К сожалению, мне не удалось найти никакой информации о том, как кеш становится недействительным, а также когда и как он сбрасывается.
Я хочу использовать это на своем сервере, но я не уверен, что он сбрасывается при перезапуске django (для меня этого было бы достаточно).
4 ответа
Покопавшись в исходном коде django, вы можете обнаружить, что загрузчики шаблонов для текущего экземпляра сервера хранятся в django.template.loader.template_source_loaders .
Когда вы используете кешированный загрузчик, там будет только один загрузчик. Таким образом, вы можете получить его и вызвать его функцию сброса, чтобы сбросить кеш шаблона.
Вот несколько фрагментов кода, сам не тестировал.
Если вы отметите django.template.loaders.cached , вы увидите, что django просто использует одну переменную template_cache для хранения имени шаблона в кэше пути к шаблону. Он не использует memcached. Поэтому он должен быть сброшен при перезапуске django.
Существующие решения, написанные здесь, сломались вокруг Django 1.9 из-за рефакторинга системы загрузчика шаблонов. Если вы попытаетесь использовать код, который импортирует template_source_loaders , и этот импорт завершится неудачно, попробуйте следующее:
Этот код предполагает, что в настройках TEMPLATES используется параметр по умолчанию 'loaders' или используется только один django.template.loaders.cached.Loader .
Только это решение будет работать гарантированно, в том числе в производственной среде, без перезапуска сервера, даже если вы используете бэкэнд 'django.template.loaders.cached.Loader' :
Это явный абсолютный импорт, который можно использовать для исправления глобальных переменных. Вы можете быть уверены, что другие ответы неверны после использования тега шаблона (кеш 'some_template' не сбрасывается после использования loader.reset() , где loader - это просто итератор template_source_loaders > импортированы путем относительного импорта.
Я использовал этот метод для аннулирования шаблонов, хранящихся в базе данных (созданной библиотекой django-dbtemplates), и которые включены в обычные шаблоны с помощью тега шаблона .
Оптимизация производительности Django проектов (часть 3)
Остальные статьи цикла:
В этой части серии мы рассмотрим важнейший подход к обеспечению высокой производительности - кэширование. Суть кэширования в том, чтобы размещать часто используемые данные в быстром хранилище для ускорения доступа к ним. Важно понять, что быстрое хранилище (например, оперативная память) часто имеет очень ограниченный объем и его нужно использовать для хранения только тех данных, которые с большой вероятностью будут запрошены.
Кэш фреймворк Django
Django предоставляет ряд средств для кэширования из коробки. Хранилище кэша настраивается при помощи словаря CACHES в settings.py :
Django предоставляет несколько встроенных бекендов для кэша, рассмотрим некоторые из них:
- DummyCache - ничего не кэширует, используется при разработке/тестировании, если нужно временно отключить кэширование,
- DatabaseCache - хранит кэш в БД, не самый быстрый вариант, но может быть полезен для хранения результатов долгих вычислений или сложных SQL запросов,
- MemcachedCache - использует Memcached в качестве хранилища, для использования этого бекенда вам понадобится поднять сервер(ы) Memcached.
Для использования в продакшене лучше всего подходит MemcachedCache и в некоторых случаях может быть полезен DatabaseCache . Также Django позволяет использовать сторонние бекенды, например, удачным вариантом может быть использование Redis в качестве хранилища для кэша. Redis предоставляет больше возможностей чем Memcached и вы скорее всего и так уже используете его в вашем проекте. Вы можете установить пакет django-redis и настроить его как бекенд для вашего кэша.
Кэширование всего сайта
Если на вашем сайте нет динамического контента, который часто меняется, то вы можете решить проблему кэширования просто - включив кэширование всего сайта. Для этого нужно добавить несколько настроек в settings.py :
После добавления показанных выше middleware первым и последним в списке, все GET и HEAD запросы будут кэшироваться на указанное в параметре CACHE_MIDDLEWARE_SECONDS время.
При необходимости вы даже можете програмно сбрасывать кэш:
Или можно сбросить кэш непосредственно в используемом хранилище. Например, для Redis:
Кэширование view
Если в вашем случае не целесообразно кэшировать весь сайт, то вы можете включить кэширование только определенных view, которые создают наибольшую нагрузку. Для этого Django предоставляет декоратор cache_page :
cache_page принимает следующие параметры:
- первый обязательный аргумент задает TTL кэша в секундах,
- cache - ключ в словаре CACHES ,
- key_prefix - префикс для ключей кэша.
Также этот декоратор можно применить в urls.py , что удобно для Class-Based Views:
Если часть контента сайта меняется в зависимости, например, от того какой пользователь аутентифицирован, то такой подход не подойдет. Для решения этой проблемы можно воспользоваться одним из вариантов описанных ниже.
Кэширование части шаблона
В предыдущей части этой серии статей было описано, что QuerySet объекты ленивые и SQL запросы не выполняются без крайней необходимости. Мы можем воспользоваться этим и закэшировать фрагменты шаблона, что позволит избежать SQL запросов на время жизни кэша. Для этого нужно воспользоваться тегом шаблона cache :
Результат добавления тегов cache в шаблон (до и после соответственно):
cache принимает следующие аргументы:
- первый обязательный аргумент означает TTL кэша в секундах,
- обязательное название фрагмента,
- не обязательные дополнительные переменные, которые идентифицируют фрагмент по динамическим данным,
- ключевой параметр using='default' , должен соответствовать ключу словаря CACHES в settings.py .
Например, если нужно, чтобы для каждого пользователя фрагмент кэшировался отдельно, то нужно передать в тег cache переменную которая идентифицирует пользователя:
При необходимости можно передавать несколько таких переменных для создания ключей на основе комбинации их значений.
Низкоуровневое кэширование
Django предоставляет доступ к низкоуровневому API кэш фреймворка. Вы можете использовать его для сохранения/извлечения/удаления данных по определенному ключу в кэше. Рассмотрим небольшой пример:
В этом фрагменте кода мы проверяем, есть ли в кэше количество авторов, которое должно быть по ключу authors_count . Если есть ( cache.get вернул не None ), то используем значение из кэша. Иначе запрашиваем значение из БД и сохраняем в кэш. Таким образом в течении времени жизни ключа в кэше мы больше не будем обращаться к БД.
Кроме результатов запросов к БД, также есть смысл кэшировать результаты сложных вычислений или обращения к внешним сервисам. Важно при этом учитывать, что данные могут изменится и в кэше будет устаревшая информация. Для того, чтобы минимизировать вероятность использования устаревших данных из кэша нужно:
- настроить адекватное TTL для кэша, которое бы соответствовало частоте изменения кэшируемых данных,
- реализовать инвалидацию кэша.
Инвалидация кеша должна происходить по событию изменения данных. Рассмотрим, как можно реализовать инвалидацию для примера с количеством авторов:
Были добавлены 2 обработчика сигналов: создание и удаление автора. Теперь при изменении количества авторов значение в кэше по ключу authors_count будет сбрасываться и в view будет запрашиваться новое количество авторов из БД.
cached_property
Кроме кэш фреймворка Django также предоставляет возможность кэшировать обращение к функции прямо в памяти процесса. Такой вид кэша возможен только для методов не принимающих никаких параметров кроме self . Такой кэш будет жить до тех пор пока существует соответствующий объект.
cached_property это декоратор входящий в Django. Результат применения его к методу, кроме кэширования, метод становится свойством и вызывается неявно без необходимости указания круглых скобок. Рассмотрим пример:
Проверим как работает свойство article_count с включенным логированием SQL:
Как вы видите, повторное обращение к свойству article_count не вызывает SQL запрос. Но если мы создадим еще один экземпляр автора, то в нем это свойство не будет закэшированно, до того как мы впервые к нему обратимся, т.к. кэш в данном случае привязан к экземпляру класса Author .
Cacheops
django-cacheops это сторонний пакет, который позволяет очень быстро внедрить кэширование запросов к БД практически не меняя код проекта. Большую часть случаев можно решить просто задав ряд настроек этого пакета в settings.py .
Рассмотрим на примере простой вариант использования этого пакета. В качестве тестового проекта будем использовать пример из прошлой части серии.
Cacheops использует Redis в качестве хранилища кэша, в settings.py нужно указать параметры подключения к серверу Redis.
Вот так просто мы добавили кэширование всех запросов к моделям из приложения blog на 15 минут. При этом cacheops автоматически настраивает инвалидацию кэша не только по времени, но и по событиям обновления данных, при помощи сигналов соответствующих моделей.
При необходимости можно настроить кэширование не только всех моделей приложения но и каждую модель отдельно и для разных запросов. Несколько примеров:
Кроме этого cacheops имеет ряд других функций, некоторые из них:
- ручное кэширование Article.objects.filter(tag=2).cache() ,
- кэширование результатов выполнения функций с привязкой к модели и автоматической инвалидацией,
- кэширование view с привязкой к модели и автоматической инвалидацией,
- кэширование фрагментов шаблона и многое другое.
Рекомендую ознакомится с README cacheops чтобы узнать подробности.
Заголовок Vary позволяет задать список названий заголовков, значения в которых будут учитываться при создании ключа кэша. Django предоставляет view декоратор vary_on_headers для управления этим заголовком.
В данном случае, для разных значений заголовка User-Agent будут разные ключи кэша.
Cache-Control
Заголовок Cache-Control позволяет задавать различные параметры управляющие механизмом кэширования. Для задания этого заголовка можно использовать встроенный в Django view декортатор cache_control .
Рассмотрим некоторые директивы заголовка Cache-Control :
- public , private - разрешает или запрещает кэширование в публичном кэше (прокси серверах и тд). Это важные директивы, которые позволяют обезопасить приватный контент, который должен быть доступен только определенным пользователям.
- no-cache - отключает кэширование, что заставляет клиент делать запрос к серверу.
- max-age - время в секундах, после которого считается, что контент устарел и его нужно запросить заново.
Last-Modified & Etag
- Last-Modified - дата и время последнего изменения ресурса.
- Etag - идентификатор версии ресурса (уникальный хэш или номер версии).
После этого при повторном обращении к ресурсу клиент должен использовать заголовки If-Modified-Since и If-None-Match соответственно. В таком случае, если ресурс не изменился (исходя из значений Etag и/или Last-Modified), то сервер вернет статус 304 без тела ответа. Это позволяет выполнять повторную загрузку ресурса только в том случае, если он изменился и тем самым съекономить время и ресурсы сервера.
Кроме кэширования, описанные выше заголовки применяются для проверки предусловий в запросах изменяющих ресурс (POST, PUT и тд). Но обсуждение этого вопроса выходит за рамки данной статьи.
Django предоставляет несколько способов задания заголовков Etag и Last-Modified . Самый простой способ - использование ConditionalGetMiddleware . Этот middleware добавляет заголовок Etag , на основе ответа view, ко всем GET запросам приложения. Также он проверяет заголовки запроса и возвращает 304, если ресурс не изменился.
Этот подход имеет ряд недостатков:
- middleware применяется сразу ко всем view проекта, что не всегда нужно,
- для проверки актуальности ресурса необходимо сгенерировать полный ответ view,
- работает только для GET запросов.
Для тонкой настройки нужно применять декоратор condition , который позволяет задавать кастомные функции для генерации заголовков Etag и/или Last-Modified . В этих функциях можно реализовать более экономный способ определения версии ресурса, например, на основе поля в БД, без необходимости генерации полного ответа view.
В функции author_updated_at выполняется простой запрос БД, который возвращает дату последнего обновления ресурса, что требует значительно меньше ресурсов чем получение всех нужных данных для view из БД и рендеринг шаблона. При этом при изменении автора функция вернет новую дату, что приведет к инвалидации кэша.
Кэширование статических файлов
Для ускорения повторной загрузки страниц рекомендуется включить кэширование статических файлов, чтобы браузер повторно не запрашивал те же скрипты, стили, картинки и тд.
В продакшн окружении вы скорее всего не будете отдавать статические файлы через Django, т.к. это медленно и не безопасно. Для этой задачи обычно используется Nginx или другой web-сервер. Рассмотрим как настроить кэширование статики на примере Nginx:
На предыдущем занятии мы с вами увидели, сколько и какие SQL-запросы выполняются для формирования HTML-страницы. Здесь важно понимать, что эта операция повторяется для каждого запроса от клиента. И представьте, что ваш сайт посещают тысячи человек в сутки, значит, каждый час таких запросов может приходить сотни. А это еще не самая большая посещаемость. Обычным делом сейчас для сайтов является нагрузка в несколько сот тысяч, а то и миллионы запросов в сутки. Поэтому, те 3-5 SQL-запросов, что мы видим в Debug Toolbar можно смело умножать на тысячи, если имеем дело с высоконагруженным сайтом.
Как еще (помимо оптимизации самих запросов) можно разгрузить сервер и еще больше уменьшить нагрузку на СУБД? Для этого был придуман такой механизм как кэширование страниц. Идея, в общем-то очевидна. Если мы строим какой-либо информационный сайт, то содержимое его страниц обновляется не часто (включая дизайн). Поэтому при многочисленных запросах к одной и той же такой странице, было бы логично первый раз сформировать ее, а при повторных обращениях, выдавать уже сформированный HTML-документ. Это намного снизит нагрузку на СУБД и сервер. Давайте посмотрим, как это делается во фреймворке Django.
Подробную информацию можно почитать на странице русскоязычной документации:
Здесь же отмечено, что кэш в Django можно реализовать либо на уровне памяти – это самый быстрый тип кэша, либо на уровне БД – наименее распространенный способ, либо на уровне файловой системы – наиболее частый вид кэша. Его мы и рассмотрим.
Вначале нужно настроить сам механизм кэширования. В документации (для файловой системы) написано, что для этого нужно в файле settings.py прописать специальный словарь CACHES, причем он отличается в зависимости от ОС сервера. Так как я работают под Windows, то мне подойдет второй вариант, где путь к корню каталога кэша прописывается с указанием имени диска:
Здесь я воспользовался переменной BASE_DIR для формирования пути к кэшу, то есть, в рабочем каталоге нашего проекта будет использоваться папка coolsite_cache для хранения кэша. Соответственно, мы ее должны добавить в проекте.
Теперь, посмотрим по документации порядок кэширования. Здесь мы видим список различных параметров кэша, то есть, настраивать кэш и для страниц сайтов целиком и отдельно для шаблонов и устанавливать время хранения кэша и так далее. Советую со всем этим ознакомиться.
Кэширование на уровне представлений
Далее, видим раздел «Кэширование на уровне представлений». И здесь сразу представлен пример реализации кэша через декоратор cache_page, у которого в круглых скобках указывается время хранения кэша в секундах.
Но, мы используем не функции, а классы представлений, поэтому кэширование будем делать на уровне URLconf. Опять же в документации представлен пример. Нам здесь вначале нужно импортировать декоратор cache_page, указать время хранения кэша и представление, связанное с конкретным URL. Давайте это сделаем. Откроем файл women/urls.py и вначале пропишем импорт:
И, в качестве примера, закэшируем главную страницу:
Все, теперь в течение 60 секунд главная страница будет использовать кэш, то есть, любые изменения, которые будут вноситься в нее в течение этого времени, не будут отображаться в браузере. Это следует учитывать, используя кэширование.
Перейдем в браузер. Сейчас для формирования главной страницы используется 386 мс и четыре SQL-запроса. Обновим ее, все без изменений, так как сейчас было выполнено формирование кэша страницы. И, если посмотреть каталог coolsite_cache, то увидим в нем файлы кэша. Обновим главную страницу еще раз и видим примерно 80 мс время формирования страницы и нулевое число SQL-запросов, то есть, был использован кэш. Через минуту страница снова будет полностью строиться по алгоритму приложения, но затем, снова браться из кэша.
Кэширование на уровне шаблонов
Вот так выполняется кэширование отдельных типов страниц на уровне представлений. Кроме того, довольно полезным является делать кэширование на уровне шаблонов. Например, мы можем выполнить кэширование отдельно сайдбара, а не всей страницы. Для этого откроем базовый шаблон base.html и вначале (или в любом удобном месте) импортируем специальный тег для реализации кэша:
И, затем, мы его используем для сайдбара:
Если теперь обновить главную страницу, то увидим всего два SQL-запроса для ее формирования. Возможно, у вас здесь возник вопрос, а почему число SQL-запросов уменьшилось, если выборка категорий прописана в классе DataMixin и команда
все равно выполняется? Все верно, эта строчка будет выполняться и при кешировании сайдбара. Но, всегда следует помнить, что это «ленивый» (отложенный) запрос, то есть, он фактически выполняется только в момент обращения к данным. Здесь же, при включенном кэше обращения не происходит, сайдбар берется уже сформированным из файла кэша, поэтому и SQL-запрос не выполняется.
Функции API низкого уровня для кэширования
- cache.set() – сохранение произвольных данных в кэш по ключу;
- cache.get() – выбор произвольных данных из кэша по ключу;
- cache.add() – заносит новое значение в кэш, если его там еще нет (иначе данная операция игнорируется);
- cache.get_or_set() – извлекает данные из кэша, если их нет, то автоматически заносится значение по умолчанию;
- cache.delete() – удаление данных из кэша по ключу;
- cache.clear() – полная очистка кэша.
Давайте воспользуемся этим функционалом и закэшируем данные рубрик. Откроем файл women/utils.py, где сначала импортируем модуль:
А, затем, в классе DataMixin будем делать выборку категорий из кэша с помощью функции get():
Далее, мы проверяем, если данные из кэша не были получены (это значит, что они туда либо не были помещены, либо истекло время кэша), то выполняем чтение из БД и заносим данные в кэш с помощью функции set().
Видео по теме
© 2021 Частичное или полное копирование информации с данного сайта для распространения на других ресурсах, в том числе и бумажных, строго запрещено. Все тексты и изображения являются собственностью сайта
Одним из способов уменьшить нагрузку на сервер является кеширование данных. Это делается путем кэширования данных после их обработки и последующего получения из кэша при следующем запросе. В этом учебнике дается подробное обсуждение Redis, объясняя, как установить Redis и как кэшировать данные в приложениях Python.
Введение в Redis и кэширование
Кэширование относится к сохранению ответа сервера на самом клиенте, так что клиенту не нужно снова и снова запрашивать сервер для одного и того же ресурса. Ответ сервера должен содержать информацию о том, как сделать кеширование, чтобы клиент кэшировал ответ на определенный период времени или вообще никогда не кэшировал ответ сервера.
Кэш, с другой стороны, является аппаратным или программным компонентом, который используется для хранения данных, поэтому будущие запросы на одни и те же данные могут быть отправлены быстрее.
В наше время, когда пользователи ожидают результаты в течение секунды, целесообразно обслуживать запросы, читая данные из кеша, что в конечном счете будет быстрее, чем чтение из более медленного хранилища данных; таким образом, производительность системы зависит от количества запросов, которые могут быть получены из кеша.
Установка Redis
Первый шаг - получить Redis и запустить локально на вашем компьютере. Самый простой способ установить Redis через диспетчер пакетов операционной системы:
Вы также можете следовать инструкциям официального сайта Redis.
Загрузите и извлеките Redis 4.0.6 tar следующим образом:
Бинарные файлы, которые теперь компилируются, доступны в каталоге src. Запустите Redis с помощью:
Вы можете взаимодействовать с Redis с помощью встроенного клиента:
Чтобы проверить, запущен ли сервер redis, выполните в терминале следующую команду:
Пример API Django
Давайте создадим наш проект Django. Наш проект сможет кэшировать все продукты в магазине, что позволяет легко и быстро извлекать данные при последующих запросах.
Чтобы использовать Redis в нашем приложении, нам нужно сделать следующее:
- Проверить, существуют ли результаты для текущего запроса в кеше.
- Если результаты есть в кеше, извлеките их.
- Если результатов не существует, извлеките их, сохраните их в кеше и затем пересылайте их запрашивающему объекту.
Требования
Создаем свой проект
Прежде чем приступить к работе, создайте каталог и установите виртуальное окружение. Виртуальное окружение позволит вам устанавливать версии библиотек, требуемые вашим приложением.
Затем активируйте виртуальное окружение и установите требования к проекту.
Создание проекта Django
Создайте новое приложение под названием store, которое будет обрабатывать управление продуктами в нашем магазине.
Добавьте приложение store и rest_framework в список установленных приложений в файле settings.py .
Создание моделей
В store/models.py мы начинаем с создания модели продукта для хранения сведений о продукте следующим образом:
Миграции
Создайте первоначальную миграцию для нашей модели продуктов и выполните синхронизацию базы данных.
Создание суперпользователя
Создайте суперпользователя, войдите в панель администратора и заполните базу данных примерами данных, которые мы будем использовать для проведения наших тестов.
Настройка Redis в приложениях Python
Чтобы использовать Redis с приложением Django, нам нужно настроить Redis для хранения данных кэша приложения. Добавьте следующее в файл settings.py :
Затем мы создадим ендпоинт, который достает все продукты из нашей базы данных. Сначала мы проверим производительность приложения с точки зрения того, сколько времени потребуется для извлечения данных из базы данных без кэширования. Затем мы реализуем другой ендпоинт, который извлекает данные из кеша и сравним производительность.
В файле store/views.py добавьте следующий код, который извлекает все продукты, существующие в базе данных.
Настройка URL-адресов
Создайте файл store/urls.py и добавьте в него следующий код.
Нам также необходимо импортировать URL-адреса из приложения users в основной файл django_cache/urls.py .
Давайте проведем тест и посмотрим, как у нас все получилось. Мы будем использовать loadtest. Если вы не знакомы с loadtest, то это инструмент для тестирования производительности.
Установка loadtest как root довольна простая:
Как видно из вышеизложенного, 55 запросов обрабатываются за секунду.
Давайте создадим еще один ендпоинт для извлечения данных после кэширования с помощью Redis. Измените файл users/views.py , чтобы в нем был следующий код:
В приведенном выше коде будет проверяться, присутствует ли нужный продукт в кеше, и если он найден, соответствующие данные будут возвращены в браузер. В случае отсутствия данных в кеше мы сначала извлекаем данные из базы данных, сохраняем их в кеше и затем возвращаем в браузер.
Обновите store/urls.py следующим образом.
Давайте проведем тесты.
В первый раз, когда вы запросите ендпоинт localhost:8000/store/cache, приложение будет запрашивать данные из базы данных и возвращать их, но последующие вызовы на этот URL будут обходить базу данных и делать запрос в кеш, поскольку данные уже доступны будут доступны в нем ,
Заключение
В этом уроке мы использовали Redis, чтобы придать иллюзию скорости нашему приложению. Мы использовали Redis для хранения результатов запросов, а затем при последующих запросах возвращали эти результаты из кеша.
Существуют и другие инструменты кеширования, такие как Memcached, похожие на Redis. Однако Redis более популярен, чем Memcached, потому что для настройки и работы в приложениях требуется всего пара минут. Redis имеет более сложные механизмы, поскольку он был описан как «хранилище структур данных», что делает его более мощным и гибким. Redis также имеет большее преимущество, потому что вы можете хранить данные в любой форме.
Надеюсь, этот учебник показал вам, как легко добавить кэш-слой в ваше приложение, и, следовательно, повысить его производительность. Кэширование должно быть тем, что нужно учитывать, когда вам нужно сократить время загрузки и затраты на сервер.
Читайте также: