Кэширование в Django: все, что вам нужно знать

Кэширование в Django: все, что вам нужно знать

14 февраля 2023 г.

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

Этот подход намного дороже с точки зрения ресурсов, чем ваши стандартные статические веб-сайты или даже сгенерированные статические сайты. Для большинства небольших и средних веб-сайтов это не является серьезной проблемой, но если вы хотите вывести свой веб-сайт на новый уровень, вам нужно максимально сократить накладные расходы.

Именно здесь на помощь приходит кеширование.

Кэшировать что-то означает сохранить результат дорогостоящего вычисления, чтобы в следующий раз не пришлось выполнять вычисление.

Вот некоторый псевдокод, объясняющий, как это будет работать для динамически генерируемой веб-страницы:

if page exists in cache:
  return cache[page.title]

else:
  generate page
  cache[page.title] = page # for next time 
  return generated page

Django поставляется с надежной системой кэширования, которая позволяет сохранять динамические страницы, поэтому их не нужно вычислять для каждого запроса. Для удобства Django предлагает различные уровни детализации кеша: вы можете кэшировать вывод определенных представлений, вы можете кэшировать только те фрагменты, которые сложно создать, или вы можете кэшировать весь сайт.

Предпосылки

Чтобы следовать этой статье, вам необходимо знать следующее:

  • Предыдущий опыт работы с Django & Python (от начального до среднего)
  • Джанго ≥2.2.6
  • Python≥3.4

Настройка системы кэширования

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

Мемкэш

Memcached – это кеш-сервер, полностью работающий в памяти. Первоначально он был разработан для обработки высоких нагрузок в LiveJournal.com, а затем был открыт Danga Interactive. Он используется такими сайтами, как Facebook и Википедия, для сокращения доступа к базе данных и значительного повышения производительности сайта.

После установки самого Memcached вам необходимо установить привязку Memcached. Доступно несколько привязок Python Memcached; два поддерживаемых Django: pylibmc и pymemcache.

Чтобы использовать Memcached с Django:

  • Задайте для BACKEND значение django.core. cache.backends.memcached.PyMemcacheCache или django.core.cache.backends.memcached.PyLibMCCache (в зависимости от выбранной привязки memcached)

* Установите для LOCATION значение ip:port значения, где ip — это IP-адрес демона Memcached, а port — это порт, на котором работает Memcached, или unix:path value, где path — это путь к файлу сокета Memcached Unix.

<код>питон # использование привязки pymemcache КЭШИ = { 'по умолчанию': { 'БЭКЭНД': 'django.core.cache.backends.memcached.PyMemcacheCache', «РАСПОЛОЖЕНИЕ»: «127.0.0.1:11211», }

Одной из замечательных особенностей Memcached является возможность совместного использования кеша несколькими серверами. Это означает, что вы можете запускать демоны Memcached на нескольких компьютерах, и программа будет рассматривать группу компьютеров как единый кеш без необходимости дублировать значения кеша на каждом компьютере.

Чтобы воспользоваться этой функцией, включите все адреса серверов в LOCATION. , либо в виде строки с точкой с запятой, разделенной запятой, либо в виде списка.

В этом примере кеш совместно используется экземплярами Memcached, работающими на IP-адресах 172.19.26.240 и 172.19.26.242, оба на порту 11211:

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.memcached.PyMemcacheCache',
        'LOCATION': [
            '172.19.26.240:11211',
            '172.19.26.242:11211',
        ]
    }
}

И последнее замечание о Memcached заключается в том, что кэширование в памяти имеет недостаток: поскольку кэшированные данные хранятся в памяти, данные будут потеряны в случае сбоя вашего сервера. Очевидно, что память не предназначена для постоянного хранения данных, поэтому не полагайтесь на кэширование в памяти как на единственное хранилище данных.

:::подсказка Без сомнения, ни один механизм кэширования Django не следует использовать в качестве постоянного хранилища – все они предназначены для кэширования, а не для хранения – но мы отмечаем это здесь, поскольку кэширование в памяти особенно временно.

:::

Кэширование базы данных

Django может хранить кэшированные данные в вашей базе данных. Это лучше всего работает, если у вас есть быстрый, хорошо индексированный сервер базы данных.

Чтобы использовать таблицу базы данных в качестве серверной части кэша:

* Установите для BACKEND значение django.core.cache. backends.db.DatabaseCache * Установите для LOCATION значение tablename, имя таблицы базы данных. Это имя может быть любым, если вы хотите, чтобы это было допустимое имя таблицы, которое еще не используется в вашей базе данных.

В этом примере имя кэш-таблицы — my_cache_table

.

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
        'LOCATION': 'my_cache_table',
    }
}

В отличие от других механизмов кэширования, кэш базы данных не поддерживает автоматическое отбраковывание записей с истекшим сроком действия на уровне базы данных. Вместо этого просроченные записи кэша удаляются каждый раз при вызове add(), set() или touch().

Создание таблицы кеша

Прежде чем использовать кеш базы данных, вы должны создать таблицу кеша с помощью этой команды:

python manage.py createcachetable

Кэширование файловой системы

Бэкэнд на основе файлов сериализует и сохраняет каждое значение кеша в виде отдельного файла. Чтобы использовать этот бэкенд, установите для BACKEND значение "django. core.cache.backends.filebased.FileBasedCache" и РАСПОЛОЖЕНИЕ< /a> в подходящий каталог. Например, чтобы сохранить кэшированные данные в /var/tmp/django_cache, используйте этот параметр:

Убедитесь, что каталог, на который указывает этот параметр, либо существует и доступен для чтения и записи, либо что он может быть создан системным пользователем, под которым работает ваш веб-сервер. Продолжая приведенный выше пример, если ваш сервер работает от имени пользователя apache, убедитесь, что каталог /var/tmp/django_cache существует и доступен для чтения и записи пользователем apache или что он может быть создан пользователем apache.

Кэширование в локальной памяти

Это кеш по умолчанию, если в вашем файле настроек не указан другой. Если вам нужны преимущества скорости кэширования в памяти, но у вас нет возможности запускать Memcached, рассмотрите возможность кэширования в локальной памяти. Этот кеш является попроцессным (см. ниже) и потокобезопасным. Чтобы использовать его, установите для параметра BACKEND значение "django.core.cache.backends.locmem.LocMemCache". Например:

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

Кэш использует стратегию отбраковки наименее недавно использованных (LRU).

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

Кэш сайта

После того как кеш настроен, самый простой способ использовать кеширование – кэшировать весь сайт. Вам потребуется добавить 'django.middleware.cache.UpdateCacheMiddleware' и 'django.middleware.cache.FetchFromCacheMiddleware' в настройку MIDDLEWARE. , как в этом примере:

FetchFromCacheMiddleware кэширует ответы GET и HEAD со статусом 200, если позволяют заголовки запросов и ответов. Ответы на запросы одного и того же URL с разными параметрами запроса считаются уникальными страницами и кешируются отдельно. Это промежуточное ПО ожидает, что на запрос HEAD ответят те же заголовки ответа, что и на соответствующий запрос GET; в этом случае он может вернуть кэшированный ответ GET на запрос HEAD.

Кроме того, UpdateCacheMiddleware автоматически устанавливает несколько заголовков в каждом . HttpResponse, которые влияют на нижестоящие кэши:

* Устанавливает заголовок Expires на текущую дату/время плюс определенный CACHE_MIDDLEWARE_SECONDS. * Устанавливает в заголовке Cache-Control максимальный возраст страницы — опять же, из настройки CACHE_MIDDLEWARE_SECONDS.

Кэш каждого просмотра

Более детальный способ использования инфраструктуры кэширования — кэширование выходных данных отдельных представлений. django.views.decorators.cache определяет декоратор cache_page, который автоматически кэширует ответ представления:

from django.views.decorators.cache import cache_page

@cache_page(60 * 15)
def my_view(request):
    ...

cache_page принимает единственный аргумент: тайм-аут кэша в секундах. В приведенном выше примере результат представления my_view() будет кэшироваться на 15 минут. (Обратите внимание, что мы записали его как 60 * 15 для удобочитаемости. 60 * 15 будет оцениваться как 900, то есть , 15 минут, умноженных на 60 секунд в минуту.)

Тайм-аут кэша, установленный cache_page, имеет приоритет над директивой max-age из заголовка Cache-Control.

Кэш для каждого просмотра, как и кеш для каждого сайта, не связан с URL-адресом. Если несколько URL-адресов указывают на одно и то же представление, каждый URL-адрес будет кэшироваться отдельно. Продолжая пример my_view, если ваш URLconf выглядит так:

urlpatterns = [
    path('foo/<int:code>/', my_view),
]

тогда запросы к /foo/1/ и /foo/23/ будут кэшироваться отдельно, как и следовало ожидать. Но после запроса определенного URL-адреса (например, /foo/23/) последующие запросы к этому URL-адресу будут использовать кеш.

По умолчанию будет использоваться кеш default, но вы можете указать любой кеш, какой хотите:

@cache_page(60 * 15, cache="other_cache")
def my_view(request):
    ...

Вы также можете указать кеш для каждого просмотра в настройках URL

from django.views.decorators.cache import cache_page

urlpatterns = [
    path('foo/<int:code>/', cache_page(60 * 15)(my_view)),
]

Кэширование фрагментов шаблона

Кэширование фрагментов шаблона дает вам максимальный контроль над тем, что кешируется на вашем сайте. Кэширование фрагментов шаблона позволяет кэшировать определенные части шаблона, в которых выполняются огромные вычисления. Мы можем сделать это с помощью тега шаблона кеша, {% cache %}.

Тег шаблона {% cache %} кэширует содержимое блока в течение заданного периода времени. Он принимает как минимум два аргумента: тайм-аут кеша в секундах и имя для присвоения фрагмента кеша. Фрагмент кэшируется навсегда, если тайм-аут равен None. Имя будет принято как есть, не используйте переменную. Например:

{% load cache %}
{% cache 500 sidebar %}
    .. sidebar ..
{% endcache %}

Иногда вам может понадобиться кэшировать несколько копий фрагмента в зависимости от некоторых динамических данных, которые появляются внутри фрагмента. Например, вам может понадобиться отдельная кэшированная копия боковой панели, использованной в предыдущем примере, для каждого пользователя вашего сайта. Сделайте это, передав один или несколько дополнительных аргументов, которые могут быть переменными с фильтрами или без них, в тег шаблона {% cache %} для уникальной идентификации фрагмента кеша:

{% load cache %}
{% cache 500 sidebar request.user.username %}
    .. sidebar for logged in user ..
{% endcache %}

API низкоуровневого кэша

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

Для таких случаев django предоставляет низкоуровневый API. Вы можете использовать этот API для хранения любых выбираемые объекты в python в ваш кеш. Вы можете кэшировать

Доступ к Cache API

Вы можете получить доступ к кэшам, настроенным в параметре CACHES, через dict-подобный объект: django.core.cache.caches. Повторные запросы одного и того же псевдонима в одном и том же потоке вернут один и тот же объект. Кэш по умолчанию доступен как django.core.cache.cache:

>>> from django.core.cache import cache

Пример:

from django.shortcuts import render
from .models import Posts
from django.core.cache import cache

def cache_recipes_view(request):
    latest_posts = cache.get('latest_posts')
    if latest_posts is None:
        latest_posts = Posts.objects.all().order_by("-updated_at")[:5]
        cache.set('recipes', latest_posts)

    return render(request, 'blog/latest_posts.html', {
        'posts': latest_posts
    })

Общие методы:

cache.set(”key”, value) — установить значение для любого доступного для выбора объекта Python с заданным ключом.

cache.get(”key”) — получить значение ключа из кеша

cache.get_or_set("ключ", value_if_not_present, 100): получить значение, если оно существует, поскольку заданное значение передается в качестве параметра для тайм-аута в 100 секунд.

cache.get_many(list_of_keys) : например: cache.get_many([’name’, ‘age’, ‘job_title’]) – вернуть несколько значений в виде словаря.

cache.set_many(dictionary_object, timeout) : например: cache.set_many({"name":"Dev", "age":18}, 100)

cache.delete(key) — удалить объект из кэш-памяти.

cache.clear() — очищает всю кэш-память.

cache.touch(key,timeout) — установить новый тайм-аут для данного ключа.

cache.incr(key, delta=1) — увеличивает ключ (только для целых чисел) на дельту значения. По умолчанию 1, если ничего не указано

cache.decr(key, delta=1) — уменьшает ключ, дельту значения. По умолчанию 1, если ничего не указано.

Заключение

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

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

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


Также опубликовано здесь.


Оригинал