Инфраструктура кеширования в 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 to 0 означает, что весь кеш очищается при 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 объединены.

Изменено в Django 3.1:

В более старых версиях 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 противном случае.

Изменено в Django 3.1:

Добавлено логическое возвращаемое значение.

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 , поэтому оно должно быть указано после тех, которые вносят такое изменение.

Copyright ©2020 All rights reserved