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

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

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

Введение в кэширование в Django

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

Базовые сценарии и варианты использования кэширования

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

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

  • Кэширование запросов к базе данных. Кэширование запросов к базе данных — хороший подход для приложений, которые часто обращаются к БД за информацией. Ряд запросов можно удовлетворить одним и тем же фрагментом кэшированных данных без необходимости обращения к базе данных. Это снижает количество обращений к базе данных и ускоряет время отклика сервера.

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

Преимущества кэширования в Django

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

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

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

  • Оптимизация ресурсов. Кэширование данных или ресурсов в веб-приложении обеспечивает доступ к ресурсам без выполнения повторяющихся операций.

Настройка проекта Django

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

$ python -m venv project

Все модули для проекта будут установлены внутри этой среды. Чтобы начать ее использовать, ее нужно активировать.

В Windows это можно сделать с помощью команды:

$ .\project\Scripts\activate

В macOS или Linux:

$ source project/bin/activate

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

$ pip install django

После успешной установки Django создадим приложение Django. Для создания проекта выполните команду:

$ django-admin startproject cachedproject

Перейдите в папку проекта. Здесь мы создадим Django-приложение. Выполните команду:

$ cd cachedproject

А затем выполните следующее:

$ python manage.py startapp cachedapplication

После успешного создания проекта и приложения нужно зарегистрировать приложение в проекте. Откройте файл settings.py и приведите список INSTALLED_APPS к следующему виду:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    # new application added
    'cachedapplication',
]

Примечание: чтобы использовать приложение в проекте Django, оно должно быть зарегистрировано в списке INSTALLED_APPS.

Чтобы убедиться в том, что фреймворк Django успешно установлен, давайте его протестируем. В терминале выполните команду:

$ python manage.py runserver

Убедитесь, что вы получили результат, показанный на этом скриншоте:

terminal output
terminal output

Теперь скопируйте URL-адрес и вставьте его в строку браузера. Должна появиться «страница успеха».

Настройка различных параметров кэширования в файле settings.py

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

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

Как следует из названия, кэширование в локальной памяти, иногда сокращенно называемое locmem, хранит кэшированные данные в оперативной памяти хостинга.

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

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

Чтобы настроить кэширование памяти в своем приложении, добавьте вот этот код в файл settings.py:

# CACHES dictionary which contains caching configurations.
CACHES = {
    # a cache alias or name. In this case, we use "default" as the alias.
    "default": {
        # Here, we're using the in-memory cache backend.
        "BACKEND": "django.core.cache.backends.locmem.LocMemCache",

        # LOCATION parameter gives a unique name or identifier to this cache instance.
        "LOCATION": "unique-snowflake",
    }
}

Файловое кэширование

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

Недостатком является относительно низкая скорость работы по сравнению с кэшированием на основе памяти.

Чтобы настроить файловое кэширование в приложении, добавьте следующий код в файл settings.py:

# A CACHES dictionary, which contains caching configurations.
CACHES = {
    # we use "default" as the alias.
    "default": {
        # Here, we're using the file-based cache backend.
        "BACKEND": "django.core.cache.backends.filebased.FileBasedCache",

        # LOCATION parameter to specify the file system path where cached data will be stored.
        "LOCATION": "/var/tmp/django_cache",
    }
}

Если вы разрабатываете под Windows, убедитесь, что путь к месту расположения начинается с буквы соответствующего диска, как показано ниже:

CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.filebased.FileBasedCache",
        "LOCATION": "C:/my/path-to/file",
    }
}

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

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

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

Чтобы использовать кэширование базы данных в своем приложении, добавьте следующий код в файл settings.py:

# CACHES dictionary, which contains caching configurations.
CACHES = {
    # we use "default" as the alias.
    "default": {
        # Here, we're using the database-backed cache backend.
        "BACKEND": "django.core.cache.backends.db.DatabaseCache",

        # Provide a LOCATION parameter to specify the database table name where cached data will be stored.
        "LOCATION": "my_cache_table",
    }
}

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

python manage.py createcachetable

Приведенная выше команда создает в базе данных таблицу в нужном формате, который ожидает система кэширования БД Django. Имя таблицы берется из LOCATION. В данном случае имя таблицы будет my_cache_table.

Кэширование с помощью Memcached

Memcached — это система кэширования in-memory, которую используют веб-разработчики и некоторые компании с целью уменьшить количество обращений к базам данных и повысить производительность сайта.

В отличие от locmem-кэша, Memcached работает как демон, то есть сервер Memcached запускается как фоновый процесс, независимо от непосредственного взаимодействия с пользователем. Поэтому Memcached необходимо установить на машине отдельно. Затем в своем Django-приложении установите и настройте один из клиентов для работы с ним, например pylibmc или pymemcache, на использование Memcached.

Приложение Django можно связать с демоном Memcached, добавив настройки кэша, местоположение, IP-адрес и другие данные, как показано ниже:

# A dictionary named CACHES, which contains caching configurations.
CACHES = {
    # "default" is the alias.
    "default": {
        # Here, we're using the Memcached cache backend.
        "BACKEND": "django.core.cache.backends.memcached.PyMemcacheCache",

        # LOCATION parameter to specify the Memcached server's IP address and port.
        "LOCATION": "127.0.0.1:11211",
    }
}

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

Есть у Memcached недостаток: поскольку данные хранятся в памяти, в случае падения сервера они будут потеряны.

Redis

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

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

После настройки сервера Redis необходимо установить клиент Python для Redis. Для установки используйте эту команду:

$ pip install django-redis

Интерфейс redis-py поддерживается Django нативно, или с помощью пакетов django-redis и redis.

Учитывая, что сервер Redis работает на localhost (127.0.0.1), порт=6379, для настройки кэширования Redis в приложении добавьте этот код в файл settings.py:

# A dictionary named CACHES, which contains caching configurations.
CACHES = {
    # "default" is the alias.
    "default": {
        # Here, we're using the Redis cache backend.
        "BACKEND": "django.core.cache.backends.redis.RedisCache",

        # A LOCATION parameter to specify the Redis server's address and port.
        "LOCATION": "redis://127.0.0.1:6379",
    }
}

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

Выполнение кэширования в Django с использованием Redis

Итак, достаточно теории. В этом разделе мы изучим, как выполнять кэширование в приложении Django. Мы рассмотрим три формы кэширования:

  • кэширование представлений (views)

  • кэширование фрагментов шаблона (template fragments)

  • кэширование сайта

Но прежде чем мы подробно разберем каждую форму кэширования, давайте сначала должным образом настроим проект. Мы создадим, зарегистрируем и заполним модели, настроим URL-адреса приложения, создадим шаблон, установим и настроим панель отладки Django.

Внутри приложения cachedapplication откройте файл models.py и приведите его к следующему виду:

from django.db import models

class Programmer(models.Model):
    name = models.CharField(max_length=50)
    dob = models.CharField(max_length=20)
    language = models.CharField(max_length=20)
    quote = models.CharField(max_length=150)

    def __str__(self):
        return f"{self.name}"

Далее откройте файл admin.py и вставьте в него этот код:

from django.contrib import admin
from .models import Programmer

admin.site.register(Programmer)

Этот фрагмент кода регистрирует модель Programmer в панели администратора Django.

Прежде чем заполнять модели, давайте выполним несколько миграций. В терминале выполните команду:

$ python manage.py makemigrations

А также эту:

$ python manage.py migrate

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

$ python manage.py createsuperuser

Эта команда предложит ввести данные суперпользователя: имя пользователя, e-mail и два пароля.

После успешного создания суперпользователя запустите локальный сервер и в браузере введите URL: http://127.0.0.1:8000/admin/. На рисунке ниже показана страница, на которую вы попадете.

dashboard login

Чтобы войти в систему, введите учетные данные суперпользователя, а после входа заполните модели данными, как показано на рисунке ниже.

the registered model in the admin interface

Внутри приложения создайте папку templates, а в ней — файл list_all.html. Пока оставьте HTML-файл пустым. Откройте файл views.py и приведите его к следующему виду:

from django.shortcuts import render
from .models import Programmer

def home(request):

    programmers = Programmer.objects.all()

    context = {
        'programmers': programmers
    }

    return render(request, 'list_all.html', context)

Теперь давайте зарегистрируем представление (view) home в файле urls.py проекта. В папке cachedproject откройте файл urls.py и вставьте в него этот код:

from django.contrib import admin
from django.urls import path, include
from cachedapplication import views

urlpatterns = [
    path('admin/', admin.site.urls),
    # the home view from the cachedapplication
    path('home/', views.home, name='home'),
]

Теперь откройте файл list_all.html и вставьте в него следующий код:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Caching</title>
    <link href="https://cdnjs.cloudflare.com/ajax/libs/mdb-ui-kit/6.4.2/mdb.min.css" rel="stylesheet"/>
</head>
<body>
    <div class="container py-5">
        <table class="table">
           <thead>
              <tr>
                 <th>Name</th>
                 <th>DOB</th>
                 <th>Language</th>
                 <th>Quote</th>
              </tr>
           </thead>
           <tbody>
              {% for programmer in programmers %}

              <tr>
                 <td>{{programmer.name}}</td>
                 <td>{{programmer.dob}}</td>
                 <td>{{programmer.language}}</td>
                 <td>{{programmer.quote}}</td>
              </tr>
              {% endfor %}
           </tbody>
        </table>
     </div>
</body>
</html>

Установим Django debug toolbar. Это пакет Python, который помогает разработчикам отслеживать производительность Django-приложений. Он предоставляет подробную информацию о запросах к базе данных, HTTP-запросах, времени отрисовки шаблонов и т.д. Итак, в терминале введите команду:

pip install django-debug-toolbar

Чтобы настроить django-debug-toolbar, откройте файл settings.py и измените список INSTALLED_APPS следующим образом:

# Application definition
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    # new application added
    'cachedapplication',

    # the debug toolbar
    'debug_toolbar',
]

Добавьте панель инструментов отладки в список MIDDLEWARE:

MIDDLEWARE = [

    # debug toolbar middleware
    'debug_toolbar.middleware.DebugToolbarMiddleware',
]

Сделайте так, чтобы промежуточное ПО панели отладки было сразу после этого:

django.middleware.csrf.CsrfViewMiddleware

Оно также должно быть перед этим:

django.contrib.auth.middleware.AuthenticationMiddleware

Добавьте конфигурации кэша Redis:

CACHES = {
    "default": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379/1",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
        }
    }
}

Также добавьте этот код в файл settings.py:

INTERNAL_IPS = [
    # ...
    '127.0.0.1',  # Add your development machine's IP address here
]

Наконец, настроим URL-адреса отладочной панели в файле urls.py. Сразу после импорта добавьте эту строку кода:

import debug_toolbar

Внутри списка urlpatterns добавьте код:

urlpatterns = [

    # debug toolbar URLS
    path('__debug__/', include(debug_toolbar.urls)),
]

Сделав это, мы можем приступать к работе. Запускаем сервер и вставляем в браузер URL: http://127.0.0.1:8000/home/. На скирншоте ниже показана страница, которую мы получим.

the debug toolbar

Каждый раз, когда вы запускаете приложение с использованием этого представления (view), оно будет выполнять SQL-запросы. В данном случае три запроса заняли 2,60 мс. Чтобы не делать каждый раз SQL-запросы к одним и тем же данным, реализуем кэширование представления.

Кэширование представлений 

Как следует из названия, кэширование представлений предполагает кэширование результатов работы отдельных представлений (views) Django. В этом разделе мы реализуем кэширование представлений. Для этого внесем несколько изменений в файл view.py. Откройте его и добавьте этот импорт:

from django.views.decorators.cache import cache_page

Прямо над представлением также добавим декоратор:

@cache_page(60*15)

(60*15) — это аргумент, передаваемый в @cache_page. Он представляет собой таймаут кэширования в секундах. Home view будет кэшироваться в течение 15 минут.

Теперь посетим это же представление и обновим страницу. Мы получим результат, показанный на скриншоте ниже.

zero sql queries

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

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

Это кэширование определенных фрагментов шаблона в проекте. Если в шаблоне есть части с большими вычислениями, то этот вид кэширования оказывается очень полезным. Для реализации такого кэширования мы используем следующие теги: {% load cache %}, {% cache %} и {% endcache %}. Тег {% cache %} принимает два аргумента: таймаут кэширования и уникальный ключ кэширования для идентификации конкретного кэшируемого фрагмента.

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

time setting

Общее время составляет 220,26 мс, а три SQL-запроса выполняются за 7,75 мс.

Теперь давайте реализуем технику кэширования. Мы кэшируем часть шаблона <div>. Откройте templates/list_all.html и измените его так:

{% load cache %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Caching</title>
    <link href="https://cdnjs.cloudflare.com/ajax/libs/mdb-ui-kit/6.4.2/mdb.min.css" rel="stylesheet"/>
</head>
<body>  
   {% cache 500 programmers %}
    <div class="container py-5">
        <table class="table">
           <thead>
              <tr>
                 <th>Name</th>
                 <th>DOB</th>
                 <th>Language</th>
                 <th>Quote</th>
              </tr>
           </thead>
           <tbody>
              {% for programmer in programmers %}

              <tr>
                 <td>{{programmer.name}}</td>
                 <td>{{programmer.dob}}</td>
                 <td>{{programmer.language}}</td>
                 <td>{{programmer.quote}}</td>
              </tr>
              {% endfor %}
           </tbody>
        </table>
     </div>
   {% endcache %}
</body>
</html>

В верхней части файла мы загружаем кэш с помощью {% load cache %} и заключаем div-часть в {% cache 500 programmers %} и {% endcache %}.

При повторном запуске проекта мы получим результат, показанный на скирншоте ниже.

better caching results: time 68.14ms, 2 queries in 2.13ms
результаты кэширования: время 68.14 мс, 2 запроса в 2.13 мс

На изображении видно, что после внедрения кэширования результаты улучшились.

Кэширование сайта

Предполагает кэширование всех страниц сайта. Для его реализации необходимо добавить в файл settings.py следующие настройки middleware:

MIDDLEWARE = [
    # …
    'django.middleware.cache.UpdateCacheMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.cache.FetchFromCacheMiddleware',
    # …
]

А также добавьте эти строки:

CACHE_MIDDLEWARE_ALIAS  = ' ' # cache alias
CACHE_MIDDLEWARE_SECONDS = 600 # number of seconds each page should be cached.
CACHE_MIDDLEWARE_KEY_PREFIX = ''  # name of site if multiple sites are used

Заключение

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

Еще больше актуальных знаний и инструментов можно освоить на онлайн-курсах OTUS под руководством экспертов в области IT. Каждый день в рамках курсов проходят открытые уроки, на которых можно узнать что-то новое и пообщаться с экспертами. Календарь уроков можно посмотреть по ссылке.

Комментарии (3)


  1. semendyaevanton
    21.11.2023 09:38
    +1

    Спасибо, было полезно. Вроде давно в отрасли, но все равно кое-что новое для себя нашел


  1. AikoASMR
    21.11.2023 09:38

    Бывает полезно, но лучше клаудфларой если данные редко меняются.


  1. Elendiar1
    21.11.2023 09:38

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

    Не упомянута библиотека django-cacheops, которая кэширует sql запросы (кверисеты) моделей приложения и автоматически инвалидирует их, что весьма удобно для простых запросов.

    Но даже и с ней не все гладко, сабквери не инвалидируются, сложные кверисеты с кучей фильтров, аннотаций, агрегаций и т.д. надо разбивать на куски для правильного кеширования, что увеличивает запросы к бд, и иногда (если кверя и так быстрая) лучше вовсе без кеширования.

    Обычно использую контейнер redis, никаких сложных настроек считай и нету.