Инфраструктура кеширования в Django ¶
Веб-сайт, созданный с помощью Django, является динамическим. Всякий раз, когда пользователь запрашивает отображение страницы, веб-сервер выполняет все виды операций - от запросов к базе данных до шаблонов отрисовки, в дополнение к бизнес-логике приложения - для создания страницы, которые увидят ваши посетители. Эти операции требуют гораздо больших вычислительных затрат, чем статический веб-сайт, иначе страницы будут считываться непосредственно из файловой системы.
Для большинства веб-приложений эта дополнительная нагрузка не является проблемой. Это связано с тем, что большинство веб-сайтов не имеют такой же популярности, как washingtonpost.com
или slashdot.org
, но представляют собой сайты малого и среднего размера с гораздо меньшим трафиком. С другой стороны, для сайтов со средним или большим трафиком важно максимально снизить нагрузку на сервер.
Здесь на помощь приходит кеширование.
Кэширование чего-либо - это процесс сохранения результата вычислительно дорогостоящей операции, чтобы вам не пришлось повторно выполнять эту операцию в следующий раз.
given a URL, try finding that page in the cache
if the page is in the cache:
return the cached page
else:
generate the page
save the generated page in the cache (for next time)
return the generated page
Django предоставляет надежную систему кэширования, которая позволяет сохранять динамические страницы, чтобы избежать их вычисления для каждого запроса. Для большей гибкости Django предлагает разные уровни детализации кеша: можно кэшировать вывод определенных представлений, только труднодоступных частей или всего вашего сайта.
Django также отлично работает с внешними кешами, такими как Squid , а также с кешами браузера. Эти типы кэшей напрямую не контролируются приложением. Однако можно предоставить им подсказки (через заголовки HTTP) о том, какие части вашего сайта следует кэшировать и как.
Смотрите также
Философии дизайна системы кэша объясняют некоторые из проектных решений для этой системы.
Настройка кеша ¶
Система кеширования требует небольшой настройки. В частности, следует указать, где должны храниться кешированные данные - в базе данных, в файловой системе или непосредственно в памяти. Это важное решение, которое влияет на производительность вашего кеша: да, некоторые системы быстрее других.
Выбор места для хранения данных кэша осуществляется путем входа в CACHES
файл настроек. Вот возможные значения CACHES
.
Memcached ¶
Memcached - это самый быстрый и наиболее эффективный тип кеша среди тех, которые изначально поддерживаются Django. Это полностью основанный на памяти кэш-сервер, изначально разработанный для поддержки высоких рабочих нагрузок LiveJournal.com, из которых впоследствии источник был открыт Danga Interactive. Крупные сайты, такие как Facebook и Wikipedia, используют его, чтобы уменьшить количество обращений к своим базам данных и повысить производительность своих сайтов в целом.
Memcached работает как служба и получает определенный объем оперативной памяти. Его единственная роль - предоставить быстрый интерфейс для добавления, извлечения и удаления данных в кеше. Все данные хранятся непосредственно в памяти, поэтому нет нагрузки, вызванной базой данных или файловой системой.
После установки Memcached следующим шагом будет установка кода привязки Memcached. В Python доступно несколько кодов привязки Memcached; два самых популярных - python-memcached и pylibmc .
Чтобы использовать Memcached с Django:
- Установите
BACKEND
наdjango.core.cache.backends.memcached.MemcachedCache
или наdjango.core.cache.backends.memcached.PyLibMCCache
(в зависимости от вашего выбора кода привязки memcached) - Задайте
LOCATION
значенияip:port
, гдеip
IP-адрес службы Memcached иport
рабочий порт Memcached, или значениеunix:path
, гдеpath
это путь к файлу коннектора Memcached Unix.
В этом примере Memcached работает на локальном (127.0.0.1) порту 11211, используя ссылку python-memcached
:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': '127.0.0.1:11211',
}
}
В этом примере Memcached доступен через локальный соединитель Unix /tmp/memcached.sock
с использованием привязки python-memcached
:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': 'unix:/tmp/memcached.sock',
}
}
Если вы используете код ссылки pylibmc
, не используйте префикс unix:/
:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
'LOCATION': '/tmp/memcached.sock',
}
}
Одна из очень интересных особенностей Memcached - это возможность распределять кеш по нескольким серверам. Это означает, что вы можете запускать службы Memcached на нескольких машинах, и программа будет рассматривать группу машин как единый кеш, без необходимости дублировать значения кеша на каждой машине. Чтобы воспользоваться этой функцией, добавьте все адреса задействованных серверов LOCATION
, разделив их запятыми или точками с запятой, или в виде списка.
В этом примере кеш распределяется по экземплярам Memcached, работающим с IP-адресами 172.19.26.240 и 172.19.26.242, оба на порту 11211:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': [
'172.19.26.240:11211',
'172.19.26.242:11211',
]
}
}
В следующем примере кэш распределен по экземплярам Memcached, работающим с IP-адресами 172.19.26.240 (порт 11211), 172.19.26.242 (порт 11212) и 172.19.26.244 (порт 11213):
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': [
'172.19.26.240:11211',
'172.19.26.242:11212',
'172.19.26.244:11213',
]
}
}
Последний момент, касающийся 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',
}
}
Создание таблицы кеширования ¶
Перед использованием кеша в базе данных вы должны создать таблицу кеша с помощью этой команды:
python manage.py createcachetable
Это создает таблицу в вашей базе данных в формате, ожидаемом системой кеширования Django. Название таблицы взято из LOCATION
.
Если вы используете несколько кешей базы данных, createcachetable
создайте таблицу для каждого кеша.
Если вы используете несколько баз данных, createcachetable
соблюдайте метод маршрутизаторов баз данных (см. Ниже).allow_migrate ()
Мол migrate
, createcachetable
не трогаем существующий стол. Это только создаст недостающие таблицы.
Чтобы просмотреть код SQL, который будет выполняться, а не на самом деле, используйте эту опцию .createcachetable --dry-run
Несколько баз данных ¶
Если вы используете кэш в базе данных с несколькими базами данных, вам также потребуется предоставить инструкции по маршрутизации для таблицы базы данных кеша. Для целей маршрутизации таблица базы данных кэша отображается как именованная модель CacheEntry
в именованном приложении django_cache
. Эта модель отсутствует в кэше модели, но ее можно использовать в контексте маршрутизации.
Например, следующий маршрутизатор перенаправляет все операции чтения из кэша cache_replica
и все операции записи из кэша в cache_primary
. Таблица кеша будет синхронизироваться только с cache_primary
:
class CacheRouter:
"""A router to control all database cache operations"""
def db_for_read(self, model, **hints):
"All cache read operations go to the replica"
if model._meta.app_label == 'django_cache':
return 'cache_replica'
return None
def db_for_write(self, model, **hints):
"All cache write operations go to primary"
if model._meta.app_label == 'django_cache':
return 'cache_primary'
return None
def allow_migrate(self, db, app_label, model_name=None, **hints):
"Only install the cache model on primary"
if app_label == 'django_cache':
return db == 'cache_primary'
return None
Если вы не укажете никакой маршрутной информации для модели кэша базы данных, механизм кэширования будет использовать базу данных default
.
И если вы не используете бэкэнд кеширования базы данных, вам не нужно беспокоиться о предоставлении инструкций по маршрутизации для модели кеширования базы данных.
Кеш файловой системы ¶
Механизм кэширования на основе файлов сериализует и сохраняет каждое значение кеша в отдельном файле. Для того, чтобы использовать этот двигатель, набор BACKEND
для "django.core.cache.backends.filebased.FileBasedCache"
и заполнения в LOCATION
соответствующем каталоге. Например, чтобы хранить данные в кэше /var/tmp/django_cache
, используйте этот параметр:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
'LOCATION': '/var/tmp/django_cache',
}
}
Если вы работаете в Windows, введите букву диска в начале пути, например:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
'LOCATION': 'c:/foo/bar',
}
}
Путь к каталогу должен быть абсолютным, то есть он должен начинаться с корня файловой системы. Косая черта в конце пути не обязательна.
Убедитесь, что каталог, указанный этим параметром, действительно существует и что он доступен для чтения и записи системному пользователю, с которым работает ваш веб-сервер. Продолжая приведенный выше пример, если ваш сервер работает с пользователем apache
, убедитесь, что каталог /var/tmp/django_cache
существует и что он доступен для чтения и записи apache
.
Кэш в локальной памяти ¶
Это кэш по умолчанию, если в вашем файле настроек нет другого кеша. Если вам нужны преимущества скорости кэширования в памяти, но у вас нет возможности запускать Memcached, рассмотрите возможность использования механизма кеширования в локальной памяти. Этот кеш предназначен для каждого процесса (см. Ниже) и управляет параллелизмом между потоками выполнения (потокобезопасный). Чтобы использовать его, установите BACKEND
значение "django.core.cache.backends.locmem.LocMemCache"
. Например :
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
'LOCATION': 'unique-snowflake',
}
}
Параметр кеширования LOCATION
используется для идентификации отдельных хранилищ в памяти. Если у вас только один кэш locmem`
, вы можете не настраивать LOCATION
; Однако, если у вас более одного хранилища локальной памяти, вам нужно будет назначить имя хотя бы одному из них, чтобы вы могли различать их.
Кэш использует стратегию очистки типа LRU (на основе даты доступа).
Обратите внимание, что у каждого процесса будет свой собственный экземпляр частного кеша, что означает невозможность межпроцессного кэширования. Это также означает, что кэш локальной памяти не особенно эффективен с точки зрения памяти, поэтому, вероятно, это не лучший выбор для производственных сред. Это хорошо для развития.
Псевдокеш (для разработки) ¶
Наконец, Django предоставляет «псевдокеш», который на самом деле не кэширует, а только реализует интерфейс кэширования без каких-либо действий.
Это полезно в случае, когда сайт широко использует кеш в производстве в разных местах, но среда тестирования или разработки не должна использовать кеш, и вы не хотите изменять код, чтобы различать типы. окружающей среды в индивидуальном порядке. Чтобы включить псевдокеш, установите BACKEND
следующее:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
}
}
Использование собственного механизма кеширования ¶
Хотя Django поставляется с несколькими механизмами кеширования, иногда может потребоваться создать собственный механизм кеширования. Чтобы использовать настраиваемый механизм кеширования с Django, вам необходимо указать путь импорта Python в ключе BACKEND
настройки CACHES
, например:
CACHES = {
'default': {
'BACKEND': 'path.to.backend',
}
}
Если вы создаете свой собственный движок, вы можете рассматривать стандартные механизмы кеширования как эталонные реализации. Код находится в каталоге django/core/cache/backends/
исходного кода Django.
Примечание: без веских причин, например, если хост не поддерживает их, рекомендуется ограничиться механизмами кеширования, включенными в Django. Они тщательно протестированы и хорошо задокументированы.
Настройки кеша ¶
Каждый механизм кеширования принимает дополнительные параметры для управления поведением кеша. Эти параметры передаются как дополнительные ключи в настройке CACHES
. Допустимые параметры:
TIMEOUT
: Тайм-аут кеша по умолчанию в секундах.300
По умолчанию этот параметр содержит секунды (5 минут). Вы можете установитьTIMEOUT
значение,None
чтобы по умолчанию ключи кеша никогда не истекали. Значение0
означает, что срок действия ключей истекает немедленно (фактически мы не «кэшируем»).OPTIONS
: Любой параметр, который необходимо передать механизму кеширования. Список допустимых параметров зависит от каждого движка, и движки, которые полагаются на стороннюю библиотеку, передают эти параметры непосредственно в базовую библиотеку кэша.Кэш - двигатели , реализующие свою собственную стратегию отбрасывания невидимых граней, то есть
locmem
, двигатели ,filesystem
иdatabase
примите следующие варианты:MAX_ENTRIES
: Максимальное количество элементов, разрешенное в кэше до удаления старых значений. Этот параметр установлен по300
умолчанию.CULL_FREQUENCY
: количество элементов, удаленных при достижении этого числаMAX_ENTRIES
. Эффективная ставка , так что указывает на чистку половины элементов , когда будет достигнуто. Этот параметр должен быть целым числом, а его значение по умолчанию - .1 / CULL_FREQUENCY
2
CULL_FREQUENCY
MAX_ENTRIES
3
Значение
CULL_FREQUENCY
to0
означает, что весь кеш очищается приMAX_ENTRIES
достижении. На некоторых движках (database
в частности) это значительно ускоряет очистку кеша за счет большего количества ошибок кеша.
Механизмы Memcached передают содержимое
OPTIONS
именованных параметров разработчикам клиентов, что позволяет более точно контролировать поведение клиентов. См. Ниже пример использования.KEY_PREFIX
: строка всегда включается (как префикс по умолчанию) во все ключи кеша, используемые сервером Django.См. Документацию по кешу для получения дополнительной информации.
VERSION
: номер версии по умолчанию ключей кеша, сгенерированных сервером Django.См. Документацию по кешу для получения дополнительной информации.
KEY_FUNCTION
: строка, содержащая путь, указывающий на функцию, которая определяет, как составить префикс, версию и ключ для формирования окончательного ключа кеша.См. Документацию по кешу для получения дополнительной информации.
В этом примере движок на основе файловой системы настроен с таймаутом 60 секунд и максимальной емкостью 1000:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
'LOCATION': '/var/tmp/django_cache',
'TIMEOUT': 60,
'OPTIONS': {
'MAX_ENTRIES': 1000
}
}
}
Вот пример конфигурации движка, основанный на python-memcached
ограничении размера объекта 2 МБ:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': '127.0.0.1:11211',
'OPTIONS': {
'server_max_value_length': 1024 * 1024 * 2,
}
}
}
Вот пример конфигурации движка, основанного на pylibmc
бинарном протоколе, аутентификации SASL и режима поведения, и который включает его ketama
:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
'LOCATION': '127.0.0.1:11211',
'OPTIONS': {
'binary': True,
'username': 'user',
'password': 'pass',
'behaviors': {
'ketama': True,
}
}
}
}
Кеш "на сайт" ¶
После настройки кеша самый простой способ использовать кеш - кэшировать весь сайт. Затем возникает вопрос о добавлении 'django.middleware.cache.UpdateCacheMiddleware'
и 'django.middleware.cache.FetchFromCacheMiddleware'
к настройке MIDDLEWARE
, как в этом примере:
MIDDLEWARE = [
'django.middleware.cache.UpdateCacheMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.cache.FetchFromCacheMiddleware',
]
Заметка
Нет, это не ошибка: промежуточное ПО «обновить» должно быть указано первым, а промежуточное ПО «получить» - последним. Детали несколько неясны, но вы можете проверить порядок в MIDDLEWARE ниже, чтобы узнать полную историю.
Затем добавьте следующие обязательные настройки в файл настроек Django:
CACHE_MIDDLEWARE_ALIAS
- Псевдоним кеша, используемый для хранения.CACHE_MIDDLEWARE_SECONDS
- количество секунд, в течение которых каждая страница должна храниться в кеше.CACHE_MIDDLEWARE_KEY_PREFIX
- Если кеш совместно используется несколькими сайтами, использующими одну и ту же установку Django, установите это значение для имени сайта или уникального имени этого экземпляра Django, чтобы избежать конфликтов ключей. Укажите пустую строку, если это к вам не относится.
По промежуточного FetchFromCacheMiddleware
слоя кэширует ответы GET и HEAD со статусом 200, если это позволяют заголовки запроса и ответа. Ответы на запросы к одному и тому же URL-адресу с разными параметрами запроса считаются отдельными страницами и кэшируются отдельно. Это промежуточное ПО ожидает, что ответ на запрос HEAD будет иметь те же заголовки, что и эквивалентный запрос GET; и в этом случае он может вернуть ответ GET из кеша во время запроса HEAD.
Кроме того, промежуточное ПО UpdateCacheMiddleware
автоматически устанавливает несколько заголовков в каждом ответе HttpResponse
:
- Заголовок
Expires
установлен на текущую дату / время плюсCACHE_MIDDLEWARE_SECONDS
. - В заголовке
Cache-Control
установлен максимальный возраст страницы, также в зависимости от настройкиCACHE_MIDDLEWARE_SECONDS
.
См. Промежуточное ПО для получения дополнительной информации о промежуточном программном обеспечении.
Если представление устанавливает собственный тайм-аут кеширования (например, с разделом max-age
в его заголовке Cache-Control
), страница будет кэшироваться до истечения тайм-аута, независимо от установленного времени. в CACHE_MIDDLEWARE_SECONDS
. Используя декораторы django.views.decorators.cache
, вы можете легко установить период ожидания для представления (декоратором cache_control()
) или отключить кеширование представления (декоратором never_cache()
). См. Раздел Использование других заголовков, чтобы узнать больше об этих декораторах.
Если USE_I18N
установлено значение True
, сгенерированный ключ кеша включает имя активного языка , см. Также процесс обнаружения языковых предпочтений Django ). Это упрощает кэширование многоязычных сайтов без необходимости самостоятельно создавать ключи кеша.
Ключи кеширования также включают активный язык, если USE_L10N
он установлен на, True
и текущий часовой пояс, когда USE_TZ
установлен на True
.
Кеш на просмотр ¶
-
django.views.decorators.cache.
cache_page
() ¶
Более точный способ использования инфраструктуры кеширования - кэширование вывода отдельных представлений. 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 минут (обратите внимание, что мы написали для удобства чтения, результат равен 15 минутам, умноженным на 60 секунд).60 * 15
900
Тайм-аут кеширования, установленный с помощью, cache_page
имеет приоритет над max-age
директивой из Cache-Control
заголовка.
Кэш на каждый просмотр, как и на сайт, строит свои ключи из URL. Если разные URL-адреса указывают на одно и то же представление, каждый URL-адрес будет иметь свой собственный кеш. Продолжая пример my_view
, если ваша конфигурация URL выглядит следующим образом:
urlpatterns = [
path('foo/<int:code>/', my_view),
]
затем запросы к /foo/1/
и /foo/23/
будут кэшироваться отдельно, как и ожидалось. Как только к одному из URL-адресов был осуществлен доступ (например /foo/23/
), последующие запросы к тому же URL-адресу будут использовать кеш.
cache_page
также принимает необязательный именованный параметр ,, cache
который указывает декоратору использовать определенный кеш (среди тех, что указаны в настройке CACHES
) при кэшировании результатов представления. По умолчанию используется кеш default
, но вы можете указать желаемый кеш:
@cache_page(60 * 15, cache="special_cache")
def my_view(request):
...
Также возможно переопределить префикс кеша для каждого представления. cache_page
принимает необязательный именованный параметр ,, key_prefix
который играет ту же роль, что CACHE_MIDDLEWARE_KEY_PREFIX
и параметр промежуточного программного обеспечения. Его можно использовать следующим образом:
@cache_page(60 * 15, key_prefix="site1")
def my_view(request):
...
Оба параметра key_prefix
и cache
могут быть указаны. Параметр key_prefix
и кнопка KEY_PREFIX
настройки CACHES
объединены.
В более старых версиях max-age
директива из Cache-Control
заголовка имела приоритет над таймаутом кеширования, установленным с помощью cache_page
.
Использование кеша для просмотра в конфигурации URL ¶
Примеры в предыдущем разделе застыли в коде кэшированного представления, потому cache_page
что функция изменяется my_view
на месте. Этот подход привязывает представление к системе кеширования, что не идеально по нескольким причинам. Например, возможно, что представления могут быть повторно использованы для другого сайта без кеша, или, может быть, в какой-то момент представления будут доступны другим людям, которые захотят использовать их без кеша. Решением этих проблем является определение кеша для каждого представления в конфигурации URL, а не вокруг самих функций представления.
Вы можете сделать это, заключив функцию представления в cache_page
оболочку при упоминании ее в конфигурации URL. Вот конфигурация, которую мы уже видели ранее:
urlpatterns = [
path('foo/<int:code>/', my_view),
]
И то же самое, в my_view
окружении cache_page
:
from django.views.decorators.cache import cache_page
urlpatterns = [
path('foo/<int:code>/', cache_page(60 * 15)(my_view)),
]
Частичный кеш шаблонов ¶
Если вам нужен еще больший контроль, все еще можно кэшировать заглушки шаблонов с помощью тега шаблона cache
. Чтобы у ваших шаблонов был доступ к этому тегу, поместите его в начало шаблона.{% load 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 %}
Если USE_I18N
установлено значение True
, кэш промежуточного ПО для каждого сайта учитывает активный язык . Для тега шаблона cache
можно использовать одну из переменных, относящихся к переводу, доступных в шаблонах, чтобы получить тот же результат:
{% load i18n %}
{% load cache %}
{% get_current_language as LANGUAGE_CODE %}
{% cache 600 welcome LANGUAGE_CODE %}
{% translate "Welcome to example.com" %}
{% endcache %}
Тайм-аут кеширования может быть переменной шаблона, если переменная шаблона является целым числом. Например, если переменная шаблона my_timeout
содержит значение 600
, следующие два примера эквивалентны:
{% cache 600 sidebar %} ... {% endcache %}
{% cache my_timeout sidebar %} ... {% endcache %}
Эта функция полезна, чтобы избежать повторений в шаблонах. Вы можете установить время ожидания в переменной в одном месте, а затем повторно использовать это значение.
По умолчанию тег cache
будет пытаться использовать кеш с именем «template_fragments». Если такого кеша не существует, он возвращается к использованию кеша по умолчанию. Вы можете определить альтернативный механизм кеширования для использования с указанным параметром using
, который должен быть последним параметром тега.
{% cache 300 local-thing ... using="localcache" %}
Указание ненастроенного имени кэша считается ошибкой.
-
django.core.cache.utils.
make_template_fragment_key
( Не fragment_name , vary_on = None ) ¶
Если вы хотите получить ключ кеша, используемый для кэшированного фрагмента, вы можете использовать make_template_fragment_key
. fragment_name
похож на второй параметр тега шаблона cache
. vary_on
это список всех дополнительных параметров, переданных в тег. Эта функция может быть полезна для аннулирования или перезаписи элемента кеша, например:
>>> from django.core.cache import cache
>>> from django.core.cache.utils import make_template_fragment_key
# cache key for {% cache 500 sidebar username %}
>>> key = make_template_fragment_key('sidebar', [username])
>>> cache.delete(key) # invalidates cached template fragment
True
API низкоуровневого кеширования ¶
В некоторых случаях кеширование всей страницы не дает существенного преимущества и даже может быть совершенно ненужным.
Например, представьте, что ваш сайт содержит представление, результаты которого зависят от нескольких дорогостоящих запросов, результаты которых меняются в разное время. В этом случае было бы не идеально использовать кэширование всей страницы, как это предлагается политиками кеширования для каждого сайта или для каждого представления, потому что нежелательно кэшировать всю страницу. результат (поскольку некоторые данные часто меняются), но все же интересно кэшировать редко меняющиеся результаты.
В таких ситуациях Django предоставляет низкоуровневый API кеширования. Вы можете использовать этот API для хранения объектов в кэше с любой степенью детализации. Вы можете поместить в этот кеш любой объект Python, который можно сериализовать pickle
: строки, словари, списки объектов модели и т. Д. (наиболее распространенные объекты Python могут быть сериализованы таким образом; дополнительную информацию о сериализации см. в документации Python pickle
).
Доступ к кешу ¶
-
django.core.cache.
caches
¶ Вы можете получить доступ к кэш - памяти , сконфигурированной в настройках
CACHES
словарного типа объекта:django.core.cache.caches
. Последовательные запросы одного и того же псевдонима в одном потоке возвращают один и тот же объект.>>> from django.core.cache import caches >>> cache1 = caches['myalias'] >>> cache2 = caches['myalias'] >>> cache1 is cache2 True
Если указанный псевдоним не существует, создается исключение
InvalidCacheBackendError
.Чтобы обеспечить параллелизм потоков, для каждого потока возвращается отдельный экземпляр механизма кэширования.
-
django.core.cache.
cache
¶ В качестве ярлыка кеш по умолчанию доступен в
django.core.cache.cache
:>>> from django.core.cache import cache
Этот объект эквивалентен
caches['default']
.
Основное использование ¶
Базовый интерфейс:
-
cache.
set
( ключ , значение , тайм-аут = DEFAULT_TIMEOUT , версия = None ) ¶ >>> cache.set('my_key', 'hello, world!', 30)
-
cache.
get
( ключ , по умолчанию = Нет , версия = Нет ) ¶ >>> cache.get('my_key') 'hello, world!'
key
должен иметь тип str
и value
может быть любым объектом Python, сериализованным с помощью "pickle".
Параметр timeout
является необязательным и по умолчанию имеет то же значение, что и timeout
соответствующий параметр серверной части, определенный в настройке CACHES
(объяснено выше). Это количество секунд, в течение которых значение должно храниться в кеше. Если timeout
установлено значение, None
остается кешированным навсегда. Один timeout
из 0
не кэширует значение.
Если объект не существует в кеше, cache.get()
верните None
:
>>> # Wait 30 seconds for 'my_key' to expire...
>>> cache.get('my_key')
None
Мы не рекомендуем сохранять буквальное значение None
в кеше, поскольку будет невозможно отличить сохраненное значение None
от ошибки кеша, обозначенной возвращаемым значением None
.
cache.get()
принимает один параметр default
. Это позволяет указать значение, которое нужно вернуть, если объект не существует в кеше:
>>> cache.get('my_key', 'has expired')
'has expired'
-
cache.
add
( ключ , значение , тайм-аут = DEFAULT_TIMEOUT , версия = None ) ¶
Чтобы добавить ключ только тогда, когда он еще не существует, используйте метод add()
. Он принимает те же параметры, что и set()
, но не пытается обновить кеш, если указанный ключ уже присутствует:
>>> cache.set('add_key', 'Initial value')
>>> cache.add('add_key', 'New value')
>>> cache.get('add_key')
'Initial value'
Если вам нужно знать, было ли add()
значение сохранено в кеше, вы можете посмотреть возвращаемое значение: было True
ли это значение сохранено, а False
если нет.
-
cache.
get_or_set
( ключ , по умолчанию , тайм-аут = DEFAULT_TIMEOUT , версия = None ) ¶
Если вы хотите получить значение ключа или установить это значение, если ключа нет в кеше, существует метод get_or_set()
. Он принимает те же параметры, что и, get()
но по умолчанию устанавливается как новое значение этого ключа в кеше, а не возвращается:
>>> cache.get('my_new_key') # returns None
>>> cache.get_or_set('my_new_key', 'my new value', 100)
'my new value'
Вы также можете передать любой исполняемый объект по умолчанию :
>>> import datetime
>>> cache.get_or_set('some-timestamp-key', datetime.datetime.now)
datetime.datetime(2014, 12, 11, 0, 15, 49, 457920)
-
cache.
get_many
( ключи , версия = Нет ) ¶
Также есть интерфейс, get_many()
который запрашивает кеш только один раз. get_many()
возвращает словарь, содержащий все запрошенные ключи, которые действительно существуют в кеше (и срок их действия не истек):
>>> cache.set('a', 1)
>>> cache.set('b', 2)
>>> cache.set('c', 3)
>>> cache.get_many(['a', 'b', 'c'])
{'a': 1, 'b': 2, 'c': 3}
-
cache.
set_many
( диктат , тайм-аут ) ¶
Чтобы установить несколько значений более эффективно, используйте set_many()
для передачи словаря пар ключ-значение:
>>> cache.set_many({'a': 1, 'b': 2, 'c': 3})
>>> cache.get_many(['a', 'b', 'c'])
{'a': 1, 'b': 2, 'c': 3}
Так же , как cache.set()
, set_many()
принимает необязательный параметр timeout
(тайм - аут).
Для движков, которые его поддерживают (memcached), set_many()
возвращает список ключей, которые не удалось вставить.
-
cache.
delete
( ключ , версия = Нет ) ¶
Вы можете явно удалить ключи с помощью, delete()
чтобы удалить определенный объект из кеша:
>>> cache.delete('a')
True
delete()
возвращается, True
если ключ был успешно удален, в False
противном случае.
Добавлено логическое возвращаемое значение.
-
cache.
delete_many
( ключи , версия = Нет ) ¶
Если вы хотите стереть несколько ключей за одну операцию, delete_many()
принимает список ключей для стирания:
>>> cache.delete_many(['a', 'b', 'c'])
-
cache.
clear
() ¶
Наконец, если вы хотите очистить все ключи из кеша, используйте cache.clear()
. Но будь осторожен; clear()
очищает все в кеше, а не только ключи, определенные вашим приложением:
>>> cache.clear()
-
cache.
touch
( ключ , тайм-аут = DEFAULT_TIMEOUT , версия = None ) ¶
cache.touch()
устанавливает новую дату истечения срока действия ключа. Например, чтобы обновить ключ, срок действия которого истекает через 10 секунд.
>>> cache.touch('a', 10)
True
Как и в случае с другими методами, этот параметр timeout
является необязательным и по умолчанию содержит параметр TIMEOUT
соответствующего двигателя в настройке CACHES
.
touch()
возвращается, True
если клавиша была нажата успешно, в противном случае False
.
-
cache.
incr
( ключ , дельта = 1 , версия = нет ) ¶
-
cache.
decr
( ключ , дельта = 1 , версия = нет ) ¶
Вы также можете увеличить или уменьшить существующий ключ , используя incr()
и методы decr()
, соответственно. По умолчанию приращение существующих значений кэша равно 1. Другие значения приращения могут быть предоставлены путем указания параметра при вызове соответствующих методов. ValueError
Если вы попытаетесь увеличить или уменьшить несуществующий ключ кеша, возникает исключение :
>>> cache.set('num', 1)
>>> cache.incr('num')
2
>>> cache.incr('num', 10)
12
>>> cache.decr('num')
11
>>> cache.decr('num', 5)
6
Заметка
Атомарность методов incr()
/ decr()
не гарантируется. Для движков, которые могут гарантировать атомарность увеличения или уменьшения (в частности, движка memcached
), операции будут фактически атомарными. Однако, если механизм не содержит собственной операции увеличения или уменьшения, функциональность будет реализована в два этапа: операция чтения, за которой следует операция обновления.
-
cache.
close
() ¶
Вы можете закрыть соединение с кешем с помощью, close()
если механизм кеширования поддерживает эту операцию.
>>> cache.close()
Заметка
Для кешей, которые не реализуют этот метод close
, это пустая операция.
Префикс ключа кеширования ¶
Если экземпляр кэша совместно используется несколькими серверами или между производственной средой и средой разработки, данные, кэшированные одним сервером, могут быть прочитаны другим сервером. Если формат кэшированных данных отличается от сервера к серверу, это может привести к проблемам, которые очень трудно диагностировать.
Чтобы этого избежать, Django предлагает возможность добавлять префиксы ко всем ключам кеша, используемым сервером. Для каждого сохраненного или прочитанного ключа кеша Django автоматически ставит перед ключом кеша значение параметра кеширования KEY_PREFIX
.
Установив KEY_PREFIX
разные значения для каждого экземпляра Django, вы можете быть уверены, что не будет конфликтов значений кеша.
Версии кеша ¶
При изменении кода, использующего кэшированные значения, вам может потребоваться очистить все кэшированные значения. Самый простой способ сделать это - очистить весь кеш, но он также может очистить значения кеша, которые по-прежнему будут действительными и полезными.
Django предоставляет более тонкий способ нацеливания на определенные значения кеша. Система кеширования Django имеет глобальный идентификатор версии, определенный в параметрах кеширования VERSION
. Значение этого параметра автоматически комбинируется с префиксом кэша и предоставленным пользователем ключом кэша для получения окончательного ключа кэша.
По умолчанию, любое чтение ключа автоматически включает версию ключа кэша сайта по умолчанию. Однако все примитивные функции кэширования предоставляют параметр version
, так что они могут указывать конкретную версию ключа кеша, будь то чтение или запись. Например :
>>> # Set version 2 of a cache key
>>> cache.set('my_key', 'hello world!', version=2)
>>> # Get the default version (assuming version=1)
>>> cache.get('my_key')
None
>>> # Get version 2 of the same key
>>> cache.get('my_key', version=2)
'hello world!'
Версия конкретного ключа может быть увеличена или уменьшена методами incr_version()
и decr_version()
. Это позволяет обновлять определенные ключи до новой версии, не затрагивая другие ключи. Продолжая предыдущий пример:
>>> # Increment the version of 'my_key'
>>> cache.incr_version('my_key')
>>> # The default version still isn't available
>>> cache.get('my_key')
None
# Version 2 isn't available, either
>>> cache.get('my_key', version=2)
None
>>> # But version 3 *is* available
>>> cache.get('my_key', version=3)
'hello world!'
Преобразование ключа кеширования ¶
Как объяснялось в предыдущих двух разделах, ключ кеша, указанный пользователем, не используется как есть - он комбинируется с префиксом кеша и версией ключа для создания окончательного ключа кеша. По умолчанию три части объединяются двоеточием для получения окончательного ключа:
def make_key(key, key_prefix, version):
return '%s:%s:%s' % (key_prefix, version, key)
Если вы хотите объединить части каким-либо другим способом или применить другую обработку к окончательному ключу (например, путем создания отпечатков пальцев частей ключа), вы можете определить пользовательскую функцию ключа.
Параметр кеширования KEY_FUNCTION
указывает путь в пунктирном синтаксисе к функции, соответствующей прототипу make_key()
(см. Выше). Если задано, эта настраиваемая функция клавиш будет использоваться вместо функции комбинации клавиш по умолчанию.
Предупреждения о ключах кеширования ¶
Memcached, наиболее часто используемый механизм кэширования в производстве, не позволяет использовать ключи кеширования, длина которых превышает 250 символов или которые содержат пробелы или управляющие символы. Такие ключи генерируют исключения. Чтобы продвигать код, совместимый с кросс-кешем, и минимизировать неприятные сюрпризы, другие встроенные механизмы кеширования генерируют предупреждение ( django.core.cache.backends.base.CacheKeyWarning
), если имя ключа вызовет ошибку с memcached
.
Если вы используете производственный механизм кеширования, который принимает более широкий диапазон ключей (настраиваемый механизм или один из встроенных механизмов, кроме memcached), и вы не хотите, чтобы вас беспокоили предупреждения CacheKeyWarning
, вы можете скрыть их с помощью этого кода размещен в модуле management
одного из приложений в INSTALLED_APPS
:
import warnings
from django.core.cache import CacheKeyWarning
warnings.simplefilter("ignore", CacheKeyWarning)
Если вместо этого вы предпочитаете определять собственную логику проверки ключей для одного из встроенных механизмов, вы можете создать подкласс, который переопределяет только метод, validate_key
и следовать инструкциям, приведенным в разделе Использование настраиваемого механизма кэширования . Например, чтобы сделать это с движком locmem
, поместите этот код в модуль:
from django.core.cache.backends.locmem import LocMemCache
class CustomLocMemCache(LocMemCache):
def validate_key(self, key):
"""Custom validation, raising exceptions or warnings as needed."""
...
… И используйте путь Python, указывающий на этот класс, в части BACKEND
вашей настройки CACHES
.
Кеши восходящего потока ¶
До сих пор этот документ был посвящен кэшированию ваших собственных данных. Но в контексте веб-разработки необходимо учитывать другой тип кеша: кэш, выполняемый «вышестоящими» кешами. Это системы, которые кэшируют страницы для пользователей еще до того, как запросы достигнут вашего веб-сервера.
Вот несколько примеров кешей восходящего потока:
- Ваш интернет-провайдер (ISP) может кэшировать некоторые страницы, поэтому, запрашивая страницу по адресу https://example.com/ , вы получаете страницу от своего провайдера без запроса, имеющего добраться до example.com напрямую. Сопровождающие example.com не знают об этом процессе кеширования. Провайдер находится между вашим веб-браузером и сервером example.com и прозрачно обрабатывает кеш.
- Ваш сайт Django может находиться за прокси-кешем, таким как Squid ( http://www.squid-cache.org/ ), который кеширует страницы для повышения производительности сети. В этом случае каждый запрос сначала обрабатывается прокси-сервером и направляется вашему приложению только в том случае, если оно сочтет это необходимым.
- Ваш веб-браузер также кэширует страницы. Если веб-страница отправляет правильные заголовки, браузер использует копию страницы в локальном кеше для любых новых запросов к той же странице, даже не запрашивая веб-страницу еще раз, чтобы увидеть, изменилась ли она.
Кеши восходящего потока действительно повышают производительность, но есть риск: многие веб-страницы создают различный контент в зависимости от того, кто аутентифицирован или в соответствии с другими параметрами, и, следовательно, системы, которые слепо кэшируют страницы только на основе в URL-адресе может предоставить неверные или конфиденциальные данные любому посетителю, просматривающему эти страницы.
Например, если вы управляете системой веб-почты, содержимое страницы «Входящие» зависит от вошедшего в систему пользователя. Если интернет-провайдер будет небрежно кэшировать ваш сайт, у первого пользователя, который войдет в систему через этого провайдера, будет кэширована собственная страница почтового ящика, доступная для последующих посетителей сайта. , Это совсем не хорошо.
К счастью, HTTP предоставляет решение этой проблемы. Существует ряд заголовков HTTP, которые сообщают кэшам восходящего потока различать кэшированный контент в соответствии с конкретными переменными и сообщают механизмам кеширования не кэшировать определенные страницы. Мы рассмотрим некоторые из этих заголовков в следующих разделах.
Использование заголовков Vary
¶
Заголовок Vary
определяет, какие заголовки запросов должен учитывать кеш для генерации ключа кеша. Например, если содержимое веб-страницы зависит от языковых предпочтений пользователя, говорят, что страница «зависит от языка».
По умолчанию система кеширования Django создает свои ключи кеша, используя, например, полный URL-адрес запроса "https://www.example.com/stories/2005/?order_by=author"
. Это означает, что каждый запрос к этому URL-адресу будет использовать одну и ту же кэшированную версию, независимо от различий между клиентом («пользователь-агент»), таких как файлы cookie или языковые предпочтения. Однако, если эта страница создает разный контент из-за некоторых различий в заголовках запроса, таких как файлы cookie, язык, подпись клиента и т. Д. затем необходимо использовать заголовок, Vary
чтобы указать механизмам кеширования, что содержимое страницы зависит от этих элементов.
Чтобы сделать это с помощью Django, используйте удобный декоратор представления django.views.decorators.vary.vary_on_headers()
, например:
from django.views.decorators.vary import vary_on_headers
@vary_on_headers('User-Agent')
def my_view(request):
...
В этом случае механизм кеширования (такой как собственное промежуточное ПО кеширования Django) будет кэшировать отдельные версии страницы для каждого типа клиента («пользовательский агент»).
Преимущество использования декоратора vary_on_headers
вместо ручной установки заголовка Vary
(написания чего-то вроде ) заключается в том, что декоратор завершает заголовок (который может уже существовать) вместо того, чтобы полностью его переопределить, что может перезаписать любое существующее содержимое заголовка.response['Vary'] = 'user-agent'
Vary
Можно отправить несколько заголовков на vary_on_headers()
:
@vary_on_headers('User-Agent', 'Cookie')
def my_view(request):
...
Это указывает восходящим кэшам различать эти два типа , что означает, что каждая комбинация типа клиента и cookie будет иметь собственное значение кеша. Например, запрос с типом клиента Mozilla
и значением cookie foo=bar
будет считаться отличным от запроса с типом клиента Mozilla
и значением cookie foo=ham
.
Поскольку изменение в зависимости от куки-файлов очень распространено, есть декоратор django.views.decorators.vary.vary_on_cookie()
. Эти два представления эквивалентны:
@vary_on_cookie
def my_view(request):
...
@vary_on_headers('Cookie')
def my_view(request):
...
Передаваемые заголовки vary_on_headers
не чувствительны к регистру; "User-Agent"
эквивалентно "user-agent"
.
Вы также можете напрямую использовать служебную функцию django.utils.cache.patch_vary_headers()
. Это определяет или завершает заголовок Vary
. Например :
from django.shortcuts import render
from django.utils.cache import patch_vary_headers
def my_view(request):
...
response = render(request, 'template_name', context)
patch_vary_headers(response, ['Cookie'])
return response
patch_vary_headers
принимает экземпляр HttpResponse
как первый параметр и список имен заголовков без учета регистра как второй параметр.
Подробнее о заголовках Vary
см.официальная спецификация от Vary .
Управление кешем: использование других заголовков ¶
Другие проблемы с кешем включают конфиденциальность данных и вопрос о том, где данные хранятся в случае каскадных кешей.
Пользователь обычно сталкивается с двумя типами кешей: его собственный кеш браузера (частный кеш) и кеш своего провайдера (общий кеш). Публичный кеш используется многими пользователями и контролируется извне. Это создает проблемы с конфиденциальными данными; вероятно, вы не хотите, чтобы номер вашего банковского счета хранился в общедоступном кеше. Поэтому веб-приложения должны иметь возможность сообщать кешам, какие данные являются частными, а какие общедоступными.
Решение состоит в том, чтобы указать, что содержимое кэшированной страницы должно быть «частным». Чтобы сделать это с Django, используйте декоратор представления cache_control()
. Пример:
from django.views.decorators.cache import cache_control
@cache_control(private=True)
def my_view(request):
...
Этот декоратор заботится об отправке соответствующих заголовков HTTP в фоновом режиме.
Обратите внимание, что параметры управления «частным» и «общедоступным» кешем являются взаимоисключающими. Декоратор гарантирует, что директива "public" будет удалена, если необходимо определить "private" (и наоборот). Примером использования этих двух правил может быть сайт блога, который предлагает как частные, так и публичные статьи. Публичные статьи можно кэшировать в любом общем кэше. В следующем коде используется django.utils.cache.patch_cache_control()
ручной способ изменения заголовка элемента управления кешем (он вызывается изнутри декоратором cache_control()
):
from django.views.decorators.cache import patch_cache_control
from django.views.decorators.vary import vary_on_cookie
@vary_on_cookie
def list_blog_entries_view(request):
if request.user.is_anonymous:
response = render_only_public_entries()
patch_cache_control(response, public=True)
else:
response = render_private_and_public_entries(request.user)
patch_cache_control(response, private=True)
return response
Также возможно управлять кешами восходящего потока другими способами (см. RFC 7234 для получения подробной информации о HTTP-кеше). Например, даже если вы не используете инфраструктуру кеширования на стороне сервера Django, все равно можно попросить клиентов кэшировать представление в течение определенного периода времени с помощью директивымаксимальный возраст :
from django.views.decorators.cache import cache_control
@cache_control(max_age=3600)
def my_view(request):
...
Если вы используете промежуточное ПО кеширования, оно уже устанавливает заголовок max-age
со значением параметра CACHE_MIDDLEWARE_SECONDS
. В этом случае значение, max_age
настроенное декоратором, имеет cache_control()
приоритет, и значения заголовка будут правильно объединены.
Любая Cache-Control
действительная директива ответа принимается в cache_control()
. Вот еще несколько примеров:
no_transform=True
must_revalidate=True
stale_while_revalidate=nombre_secondes
no_cache=True
Полный список известных руководящих принципов можно найти в реестре IANA (обратите внимание, что не все из них применимы к ответам).
Если вы хотите использовать заголовки для полного отключения кеширования, существует never_cache()
декоратор представления, который добавляет необходимые заголовки, чтобы гарантировать, что ответ не будет кэшироваться браузерами или другими кешами. Пример:
from django.views.decorators.cache import never_cache
@never_cache
def myview(request):
...
Заказать в MIDDLEWARE
¶
Если вы используете промежуточное ПО кэширования, важно поместить каждый из двух элементов в правильное место при настройке MIDDLEWARE
. Это связано с тем, что промежуточное программное обеспечение кеширования должно знать, от каких заголовков должно отличаться хранилище кеша. По промежуточного слоя всегда что-то добавляет в заголовок ответа, Vary
насколько это возможно.
UpdateCacheMiddleware
выполняется во время фазы ответа, когда промежуточное ПО выполняется в обратном порядке, а элемент в верхней части списка выполняется последним во время фазы ответа. Поэтому вы должны позаботиться о размещении, UpdateCacheMiddleware
прежде чем какое-либо другое промежуточное программное обеспечение может что-то добавить в заголовок Vary
. Затронуты следующие промежуточные модули:
SessionMiddleware
ДобавитьCookie
GZipMiddleware
ДобавитьAccept-Encoding
LocaleMiddleware
ДобавитьAccept-Language
FetchFromCacheMiddleware
, с другой стороны, выполняется на этапе запроса, когда промежуточное программное обеспечение запускается сверху вниз, а элемент, находящийся в верхней части списка, запускается первым на этапе запроса. Промежуточное ПО FetchFromCacheMiddleware
также должно запускаться после того, как любое другое промежуточное ПО изменяет заголовок Vary
, поэтому оно должно быть указано после тех, которые вносят такое изменение.