Django-Admin.ru

Redis в Django: кеширование и сессии

Как подключить Redis к Django: встроенный RedisCache или django-redis, настройка CACHES, cache_page, кеш шаблонов, API кеша, сессии в Redis и Docker.

Редакция Django-Admin.ru
· 18 мин чтения

Содержание · 15 разделов
  1. Что Redis делает в проекте Django
  2. Как запустить Redis для разработки
  3. Встроенный RedisCache или django-redis
  4. Настройка CACHES со встроенным бэкендом
  5. Установка клиента
  6. Блок CACHES в settings.py
  7. Пароль, TLS и номер базы
  8. Настройка django-redis
  9. Ошибка unexpected keyword argument ‘CLIENT_CLASS’
  10. Низкоуровневый API кеша
  11. Кеширование данных и сброс при изменении
  12. Возможности django-redis сверх стандартного API
  13. Кеширование view: cache_page
  14. Кеширование фрагментов шаблона
  15. Сессии Django в Redis
  16. Redis как брокер Celery
  17. Redis для Django Channels
  18. Redis и Valkey в Docker Compose
  19. Частые ошибки
  20. ConnectionError: Error 111 connecting to 127.0.0.1:6379
  21. Данные на сайте не обновляются
  22. После очистки кеша всех разлогинило
  23. redis-cli не показывает ключи
  24. Частые вопросы
  25. Redis или Memcached для Django?
  26. Как посмотреть, что лежит в кеше?
  27. Как отключить кеш в тестах и при разработке?
  28. Можно ли автоматически кешировать запросы ORM?
  29. Работает ли Redis на Windows?
  30. Итог

Связка Django и Redis чаще всего нужна для кеша: Redis хранит готовые страницы, фрагменты шаблонов и результаты тяжёлых запросов в оперативной памяти, а Django читает их за доли миллисекунды вместо обращения к базе. С Django 4.0 в коробке есть бэкенд django.core.cache.backends.redis.RedisCache — достаточно установить клиент redis и прописать адрес сервера в настройке CACHES. Сторонний пакет django-redis нужен, когда требуются возможности сверх стандартного API кеша: TTL ключа, поиск и удаление по шаблону, блокировки, работа при недоступном Redis.

Быстрый ответ: выполните python -m pip install "redis[hiredis]", добавьте в settings.py блок CACHES с "BACKEND": "django.core.cache.backends.redis.RedisCache" и "LOCATION": "redis://127.0.0.1:6379/2", затем используйте cache.get()/cache.set(), декоратор cache_page или тег {% cache %}. Для сессий в Redis добавьте SESSION_ENGINE = "django.contrib.sessions.backends.cache". Код ниже проверен на Django 6.1 с redis-py 8.1 и django-redis 7.0; для Django 5.2 LTS настройки те же.

Что Redis делает в проекте Django

Один сервер Redis обычно закрывает четыре задачи. Каждая подключается своей настройкой, и каждой удобно выделить отдельную логическую базу (по умолчанию в Redis их 16, с номерами 0–15):

ЗадачаНастройка DjangoБаза в примерах
Брокер задач CeleryCELERY_BROKER_URL0
Результаты задач CeleryCELERY_RESULT_BACKEND1
Кеш страниц и данныхCACHES["default"]2
Сессии пользователейCACHES["sessions"] + SESSION_ENGINE3
Слой каналов Django ChannelsCHANNEL_LAYERS4

Разделение баз защищает данные: метод cache.clear() выполняет команду FLUSHDB и стирает всю базу, а не только ключи кеша. Если кеш и сессии лежат в одной базе, очистка кеша разлогинит всех пользователей; если там же очередь Celery — пропадут задачи.

Как запустить Redis для разработки

Проще всего — в Docker, одна команда на любой ОС:

docker run -d --name redis -p 6379:6379 redis:8
docker exec -it redis redis-cli ping
PONG

На Ubuntu и Debian Redis ставится пакетом: sudo apt install redis-server, на macOS — brew install redis. Официальной сборки для Windows нет: запускайте Redis в Docker Desktop или WSL.

Вместо Redis можно взять Valkey — форк Redis 7.2 под лицензией BSD, который поддерживает Linux Foundation. Он совместим по протоколу, поэтому все примеры статьи работают с ним без изменений: клиент тот же (redis), схема адреса тоже redis://. Образ — valkey/valkey:9, утилита проверки — valkey-cli ping.

Встроенный RedisCache или django-redis

Оба бэкенда работают через клиент redis-py и реализуют стандартный API кеша Django, поэтому cache_page, {% cache %} и сессии работают с любым из них. Разница — в дополнительных возможностях и формате настроек.

Критерийdjango.core.cache.backends.redis.RedisCachedjango_redis.cache.RedisCache
Установкаredis (встроен в Django с 4.0)django-redis (тянет redis)
Поддерживаемые версии Djangoлюбая с 4.07.0: Django 5.2+
Формат OPTIONSаргументы пула redis-py в нижнем регистресвои ключи в верхнем регистре
Чтение с репликда: список адресов, первый — ведущийда, плюс клиент для Sentinel
TTL ключа, expire, persistнетcache.ttl(), cache.expire(), cache.persist()
Поиск и удаление по шаблонунетcache.keys(), cache.iter_keys(), cache.delete_pattern()
Распределённая блокировканетcache.lock()
Сырой клиент Redisтолько через приватные атрибутыget_redis_connection("default")
Работа при упавшем Redisисключение ConnectionErrorопция IGNORE_EXCEPTIONS: промах вместо ошибки
Сжатие и сериализаторыpickle, свой сериализатор через OPTIONSzlib, gzip, lzma, lz4, zstd; JSON, msgpack

Как выбрать:

  • Встроенный бэкенд — для типичного проекта: кеш view, фрагментов, данных и сессии. Меньше зависимостей, поддерживается командой Django.
  • django-redis — если нужно сбрасывать группу ключей по маске (product:*), смотреть TTL, брать блокировку на время импорта, работать с Redis Sentinel или продолжать отдавать сайт, когда Redis недоступен.

Код, использующий только стандартный API (get, set, delete, get_or_set), переносится между бэкендами правкой одной строки BACKEND.

Настройка CACHES со встроенным бэкендом

Установка клиента

Активируйте виртуальное окружение проекта и установите клиент вместе с ускоренным парсером hiredis — его рекомендует документация Django, а redis-py подхватывает его автоматически:

# Windows
py -m pip install "redis[hiredis]"

# macOS / Linux
python3 -m pip install "redis[hiredis]"

Django 6.x требует redis-py версии 5.1 или новее.

Блок CACHES в settings.py

Адрес сервера выносите в переменную окружения: локально это 127.0.0.1, в Docker — имя сервиса, на сервере — внутренний адрес. Подробнее о таком подходе — в статье про настройки Django.

# config/settings.py
import os

REDIS_URL = os.environ.get("REDIS_URL", "redis://127.0.0.1:6379")

CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.redis.RedisCache",
        "LOCATION": f"{REDIS_URL}/2",
        "KEY_PREFIX": "mysite",
        "TIMEOUT": 300,
        "OPTIONS": {
            "socket_connect_timeout": 2,
            "socket_timeout": 2,
        },
    },
}
  • LOCATION — адрес в формате redis-py: redis:// для обычного TCP, rediss:// для TLS, unix:///run/redis.sock?db=2 для Unix-сокета. В схемах redis:// и rediss:// число после слеша — номер базы.
  • KEY_PREFIX — префикс всех ключей. Нужен, если один Redis обслуживает несколько проектов.
  • TIMEOUT — время жизни записей по умолчанию в секундах (300, если не указано). None — хранить бессрочно, 0 — не кешировать.
  • OPTIONS встроенный бэкенд передаёт как аргументы пулу соединений redis-py. Таймауты в 2 секунды не дадут воркеру Gunicorn зависнуть, если Redis перестал отвечать.

Чтобы ограничить число соединений и ждать свободное вместо ошибки, подключите блокирующий пул:

"OPTIONS": {
    "pool_class": "redis.BlockingConnectionPool",
    "max_connections": 50,
},

Пароль, TLS и номер базы

Логин и пароль указывают прямо в адресе: redis://user:password@redis.example.com:6379/2, для TLS — rediss://.... Номер базы задавайте в одном месте. Если он есть и в адресе, и в OPTIONS ("db": "3"), redis-py возьмёт значение из адреса, а опцию молча проигнорирует.

Для схемы с репликами передайте список: запись идёт на первый сервер, чтение — на случайную реплику.

"LOCATION": [
    "redis://10.0.0.10:6379/2",  # ведущий
    "redis://10.0.0.11:6379/2",  # реплика
],

Настройка django-redis

python -m pip install django-redis
# config/settings.py
CACHES = {
    "default": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": f"{REDIS_URL}/2",
        "KEY_PREFIX": "mysite",
        "OPTIONS": {
            "SOCKET_CONNECT_TIMEOUT": 2,
            "SOCKET_TIMEOUT": 2,
            "IGNORE_EXCEPTIONS": True,
            "CONNECTION_POOL_KWARGS": {"max_connections": 50},
        },
    },
}
  • IGNORE_EXCEPTIONS: True — если Redis недоступен, cache.get() вернёт None, а cache.set() ничего не сделает. Сайт продолжит работать медленнее, без ошибки 500. Чтобы такие сбои попадали в логи, добавьте DJANGO_REDIS_LOG_IGNORED_EXCEPTIONS = True. Не включайте эту опцию для алиаса, где лежат сессии: при сбое пользователи будут молча терять сессию.
  • COMPRESSOR — сжатие больших значений, например "django_redis.compressors.zlib.ZlibCompressor".
  • SERIALIZER — формат хранения. По умолчанию pickle; "django_redis.serializers.json.JSONSerializer" делает значения читаемыми для сервисов на других языках, но сохраняет только JSON-совместимые типы.

Ошибка unexpected keyword argument ‘CLIENT_CLASS’

Частая ошибка — в примерах из интернета смешаны два бэкенда: BACKEND встроенный, а OPTIONS от django-redis:

CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.redis.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379/2",
        "OPTIONS": {"CLIENT_CLASS": "django_redis.client.DefaultClient"},  # ошибка
    }
}

Встроенный бэкенд передаёт ключи OPTIONS в redis-py, и первое обращение к кешу падает:

TypeError: AbstractConnection.__init__() got an unexpected keyword argument 'CLIENT_CLASS'

Решение: либо уберите CLIENT_CLASS, IGNORE_EXCEPTIONS, SERIALIZER и другие ключи в верхнем регистре, либо смените BACKEND на django_redis.cache.RedisCache.

Низкоуровневый API кеша

Объект cache из django.core.cache — кеш с алиасом default; другие алиасы доступны через caches["имя"]. Методы одинаковы для всех бэкендов:

from django.core.cache import cache

cache.set("greeting", "Привет", timeout=60)
cache.get("greeting")                    # → 'Привет'
cache.get("missing")                     # → None
cache.get("missing", "по умолчанию")     # → 'по умолчанию'
cache.add("greeting", "другое")          # → False: ключ уже есть
cache.get_or_set("answer", lambda: 42, timeout=60)  # → 42

cache.set("views", 0)
cache.incr("views"), cache.incr("views", 10)  # → (1, 11)

cache.set_many({"a": 1, "b": 2})
cache.get_many(["a", "b", "c"])          # → {'a': 1, 'b': 2}
cache.touch("a", 10)                     # → True: новый срок жизни
cache.delete("a"), cache.delete("a")     # → (True, False)
cache.has_key("b")                       # → True

Значения сериализуются через pickle, поэтому в кеш можно класть словари, списки, экземпляры моделей. Целые числа встроенный бэкенд хранит без pickle, поэтому incr() выполняется атомарной командой Redis INCRBY.

В Redis ключи получают префикс и номер версии в формате префикс:версия:ключ:

redis-cli -n 2 --scan
mysite:1:greeting
mysite:1:answer
mysite:1:views
mysite:1:b

В асинхронных view используйте методы с префиксом a: await cache.aget("key"), await cache.aset("key", value). Сейчас они выполняют синхронный код в отдельном потоке — нативной асинхронной работы с Redis в Django пока нет.

Кеширование данных и сброс при изменении

Типовой сценарий — кешировать результат запроса к базе и удалять ключ, когда данные меняются. get_or_set вызовет функцию только при промахе:

# shop/services.py
from django.core.cache import cache

from .models import Category

MENU_KEY = "menu:categories"


def get_menu():
    return cache.get_or_set(
        MENU_KEY,
        lambda: list(Category.objects.values("id", "name")),
        timeout=60 * 60,
    )

Перед записью превращайте QuerySet в список (list(...)): QuerySet ленивый, и без принудительного выполнения в кеш попадёт не тот результат, который вы ожидаете.

Сброс — через сигналы модели:

# shop/signals.py
from django.core.cache import cache
from django.db.models.signals import post_delete, post_save
from django.dispatch import receiver

from .models import Category
from .services import MENU_KEY


@receiver([post_save, post_delete], sender=Category)
def drop_menu_cache(sender, **kwargs):
    cache.delete(MENU_KEY)
# shop/apps.py
from django.apps import AppConfig


class ShopConfig(AppConfig):
    name = "shop"

    def ready(self):
        from . import signals  # noqa: F401

Проверка в shell:

>>> Category.objects.create(name="Кружки")
>>> get_menu(); get_menu()   # второй вызов не обращается к базе
[{'id': 1, 'name': 'Кружки'}]
>>> Category.objects.create(name="Футболки")
>>> get_menu()
[{'id': 1, 'name': 'Кружки'}, {'id': 2, 'name': 'Футболки'}]

Сигналы не срабатывают на QuerySet.update() и bulk_create() — после массовых операций удаляйте ключ вручную. Кеш не заменяет оптимизацию запросов: если страница делает сотню SQL-запросов из-за связей, сначала устраните проблему N+1.

Возможности django-redis сверх стандартного API

С бэкендом django-redis у объекта cache появляются методы, которых нет в стандартном API Django:

from django.core.cache import cache

cache.set("product:1", "Кружка", timeout=300)
cache.set("product:2", "Футболка", timeout=300)
cache.set("user:1", "Анна", timeout=None)

cache.ttl("product:1"), cache.ttl("user:1"), cache.ttl("nope")  # → (300, None, 0)
sorted(cache.keys("product:*"))   # → ['product:1', 'product:2']
cache.delete_pattern("product:*")  # → 2

cache.expire("user:1", 120)       # → True
cache.persist("user:1")           # → True: снова бессрочный

# set с nx=True записывает, только если ключа нет (команда SETNX)
cache.set("lock:report", 1, timeout=60, nx=True)  # → True
cache.set("lock:report", 1, timeout=60, nx=True)  # → False

cache.ttl() возвращает None для бессрочного ключа и 0 для отсутствующего. На больших базах вместо keys() используйте генератор iter_keys(): он перебирает ключи курсором SCAN и не блокирует сервер.

Распределённая блокировка не даст двум воркерам одновременно запустить один и тот же импорт:

with cache.lock("import-prices", timeout=30):
    import_prices()

Для команд, которых нет в API кеша (хеши, списки, счётчики с HINCRBY), возьмите клиент redis-py из того же пула соединений:

from django_redis import get_redis_connection

r = get_redis_connection("default")
r.hset("stats", mapping={"visits": 10})
r.hgetall("stats")  # → {b'visits': b'10'}

Ключи, записанные напрямую через клиент, не получают KEY_PREFIX и версию. Команды redis-py подробно разобраны в статье «Redis в Python: библиотека redis-py».

Кеширование view: cache_page

Декоратор cache_page сохраняет весь ответ view на заданное число секунд. Повторный запрос того же URL отдаётся из Redis без выполнения view:

# blog/views.py
from django.views.decorators.cache import cache_page


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

Классовые view и чужие view удобнее оборачивать в urls.py:

# blog/urls.py
from django.urls import path
from django.views.decorators.cache import cache_page

from .views import ArticleListView

urlpatterns = [
    path("articles/", cache_page(60 * 15)(ArticleListView.as_view())),
]

Ключ кеша строится из полного URL с GET-параметрами, поэтому ?page=2 кешируется отдельно. В ответ добавляется заголовок Cache-Control: max-age=900, и браузер тоже начинает кешировать страницу. Дополнительные параметры: cache="pages" — записывать в другой алиас из CACHES, key_prefix="v2" — свой префикс.

Важно: cache_page не учитывает, кто запросил страницу. Не вешайте его на view с именем пользователя, корзиной, личными данными — иначе один посетитель увидит страницу другого. Для страниц с CSRF-формой добавьте декоратор csrf_protect под cache_page: тогда заголовок Vary: Cookie попадёт в ответ до сохранения в кеш, и посетители не получат чужой CSRF-токен.

Чтобы закешировать весь сайт, Django предлагает пару middleware: UpdateCacheMiddleware первым в MIDDLEWARE и FetchFromCacheMiddleware последним. Это подходит только для полностью публичных сайтов без авторизации.

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

Если страница персональная, кешируйте её тяжёлые части — меню, сайдбар, блок «популярное». Тег {% cache %} принимает время жизни, имя фрагмента и необязательные параметры, по которым хранятся отдельные копии:

{% load cache %}

{% cache 600 sidebar %}
  <aside>
    {% for category in categories %}
      <a href="{{ category.get_absolute_url }}">{{ category.name }}</a>
    {% endfor %}
  </aside>
{% endcache %}

{% cache 600 user_menu request.user.pk %}
  ...
{% endcache %}

Второй фрагмент хранится отдельно для каждого пользователя. Тег {% cache %} кеширует только HTML: если QuerySet categories передан в контекст и не используется вне фрагмента, запрос к базе при попадании в кеш не выполнится. Другой алиас указывают параметром using="pages". Подробнее о синтаксисе — в статье про шаблоны Django.

Сбросить фрагмент можно по ключу, который строит make_template_fragment_key:

from django.core.cache import cache
from django.core.cache.utils import make_template_fragment_key

key = make_template_fragment_key("user_menu", [user.pk])
cache.delete(key)

После обновления до Django 6.1 изменился формат ключей для фрагментов с параметрами и для страниц с Vary: первый запрос к ним будет промахом, кеш заполнится заново.

Сессии Django в Redis

По умолчанию Django хранит сессии в таблице базы данных и читает её на каждом запросе. Перенос сессий в Redis снимает эту нагрузку. Есть два движка:

ДвижокГде хранятся данныеЧто будет при потере данных Redis
django.contrib.sessions.backends.cacheтолько в кешевсе пользователи разлогинятся
django.contrib.sessions.backends.cached_dbв базе и в кеше; чтение из кешасессии подтянутся из базы

Движок cache быстрее: запись идёт только в Redis, а clearsessions по расписанию не нужен — Redis сам удаляет ключи с истёкшим сроком. Но сессии исчезнут при перезапуске Redis без сохранения на диск или при вытеснении ключей из-за нехватки памяти. Если вы не настроили персистентность (AOF или RDB-снимки), выбирайте cached_db: запись идёт в базу и в кеш, а если запись в кеш не удалась, ошибка только логируется.

Выделите сессиям отдельный алиас и отдельную базу Redis, чтобы cache.clear() их не трогал:

# config/settings.py
CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.redis.RedisCache",
        "LOCATION": f"{REDIS_URL}/2",
    },
    "sessions": {
        "BACKEND": "django.core.cache.backends.redis.RedisCache",
        "LOCATION": f"{REDIS_URL}/3",
    },
}

SESSION_ENGINE = "django.contrib.sessions.backends.cache"
SESSION_CACHE_ALIAS = "sessions"

Срок жизни сессии по-прежнему задаёт SESSION_COOKIE_AGE (по умолчанию две недели): Django передаёт его как TTL ключа. В Redis ключ выглядит как :1:django.contrib.sessions.cache плюс 32-символьный идентификатор сессии из cookie. Код view при смене движка не меняется: request.session["cart"] = [...], вход и выход пользователей работают как раньше.

Redis как брокер Celery

Celery использует Redis как очередь задач: Django кладёт задачу, воркер забирает. Настройка — две строки в settings.py и пакет celery[redis]:

CELERY_BROKER_URL = f"{REDIS_URL}/0"
CELERY_RESULT_BACKEND = f"{REDIS_URL}/1"

Брокер к кешу Django отношения не имеет и настраивается отдельно от CACHES. Подключение Celery, задачи, расписание и запуск воркера разобраны в статье «Celery в Django».

Redis для Django Channels

Django Channels добавляет WebSocket, а слой каналов (channel layer) передаёт сообщения между процессами — например, рассылает сообщение чата всем подключённым клиентам. В продакшне этот слой работает через Redis и пакет channels-redis:

# config/settings.py
CHANNEL_LAYERS = {
    "default": {
        "BACKEND": "channels_redis.core.RedisChannelLayer",
        "CONFIG": {"hosts": [f"{REDIS_URL}/4"]},
    },
}

Есть и вариант на механизме Pub/Sub — channels_redis.pubsub.RedisPubSubChannelLayer. Консьюмеры, группы и маршрутизация описаны в статье «Django Channels: WebSocket и асинхронность».

Redis и Valkey в Docker Compose

В Docker Compose Redis — отдельный сервис, и Django подключается к нему по имени сервиса — redis:

services:
  redis:
    image: redis:8
    command:
      - redis-server
      - --appendonly
      - 'yes'
      - --maxmemory
      - 256mb
      - --maxmemory-policy
      - volatile-lru
    volumes:
      - redis_data:/data
    healthcheck:
      test: ['CMD', 'redis-cli', 'ping']
      interval: 5s
      timeout: 3s
      retries: 10
    restart: unless-stopped

  web:
    build: .
    env_file: .env
    environment:
      REDIS_URL: redis://redis:6379
    depends_on:
      redis:
        condition: service_healthy

volumes:
  redis_data:
  • --appendonly yes и том redis_data сохраняют данные между перезапусками контейнера. Это важно для сессий в движке cache и для очереди Celery; для чистого кеша персистентность не обязательна.
  • --maxmemory ограничивает память. Политика volatile-lru при нехватке памяти вытесняет только ключи со сроком жизни — записи кеша, — и не трогает очередь Celery, у ключей которой срока нет. С allkeys-lru Redis может удалить задачи из очереди. Если Redis нужен только для кеша, подойдёт allkeys-lru.
  • depends_on с condition: service_healthy запускает Django после того, как Redis ответил на ping.

Для Valkey замените образ и утилиты — параметры командной строки совпадают:

services:
  redis:
    image: valkey/valkey:9
    command: [valkey-server, --appendonly, 'yes']
    healthcheck:
      test: ['CMD', 'valkey-cli', 'ping']

Имя сервиса можно оставить redis, тогда REDIS_URL не меняется. Dockerfile, PostgreSQL и статика в контейнерах разобраны в статье «Django в Docker». Наружу порт 6379 на сервере не публикуйте: Django обращается к Redis по внутренней сети Compose, а открытый Redis без пароля — частая причина взломов. Общая схема продакшна с Gunicorn и Nginx — в руководстве по деплою Django.

Частые ошибки

ConnectionError: Error 111 connecting to 127.0.0.1:6379

redis.exceptions.ConnectionError: Error 111 connecting to 127.0.0.1:6379. Connection refused.

Redis не запущен или адрес неверный (на Windows код ошибки 10061). Проверьте redis-cli ping. В Docker внутри контейнера 127.0.0.1 указывает на сам контейнер Django — используйте имя сервиса: redis://redis:6379.

Данные на сайте не обновляются

Сработал кеш: cache_page, {% cache %} или get_or_set отдают сохранённую копию до истечения TIMEOUT. Удаляйте ключи в сигналах post_save/post_delete или уменьшите время жизни. Для отладки временно переключите кеш на django.core.cache.backends.dummy.DummyCache — он принимает все вызовы и ничего не хранит.

После очистки кеша всех разлогинило

Сессии лежат в той же базе Redis, что и кеш, а cache.clear() выполнил FLUSHDB. Вынесите сессии в отдельный алиас с другим номером базы, как в разделе про сессии.

redis-cli не показывает ключи

redis-cli по умолчанию открывает базу 0. Укажите номер базы из LOCATION: redis-cli -n 2 --scan. И учитывайте префикс: ключ greeting в Redis называется mysite:1:greeting.

Частые вопросы

Redis или Memcached для Django?

Для нового проекта чаще выбирают Redis: один сервер закрывает кеш, сессии, брокер Celery и слой Channels, умеет сохранять данные на диск и хранит значения до 512 МБ. Memcached проще и подходит, если нужен только кеш и потеря данных при перезапуске не важна. Оба бэкенда встроены в Django, и код с API кеша работает с ними одинаково.

Как посмотреть, что лежит в кеше?

Выполните redis-cli -n 2 --scan --pattern 'mysite:*' — команда перебирает ключи курсором и не блокирует сервер, в отличие от KEYS *. Значения сериализованы pickle, поэтому читать их удобнее из python manage.py shell через cache.get("ключ"). Команда redis-cli monitor показывает все запросы к Redis в реальном времени — полезно для отладки, но на нагруженном сервере её не оставляйте.

Как отключить кеш в тестах и при разработке?

В тестовых настройках задайте DummyCache или LocMemCache (кеш в памяти процесса) — тогда тесты не зависят от запущенного Redis. Для отдельного теста подойдёт декоратор override_settings(CACHES=...). LocMemCache не годится для продакшна: у каждого процесса Gunicorn свой кеш, и данные между ними не синхронизируются.

Можно ли автоматически кешировать запросы ORM?

Да, этим занимаются сторонние пакеты. django-cachalot кеширует результаты запросов и сбрасывает их при любой записи в таблицу; работает с Redis, Memcached и другими бэкендами кеша Django. django-cacheops работает только с Redis и даёт тонкую настройку по моделям и операциям, но в описании последней версии заявлена поддержка Django до 5.2. Оба добавляют неочевидную логику сброса — начинайте с ручного кеширования горячих мест.

Работает ли Redis на Windows?

Официальной сборки Redis для Windows нет. Для разработки запустите его в Docker Desktop или в WSL — Django на Windows подключается к redis://127.0.0.1:6379 так же, как на Linux. Для Valkey подходят те же варианты.

Итог

  • Для кеша в Django достаточно встроенного бэкенда django.core.cache.backends.redis.RedisCache и пакета redis[hiredis]; django-redis подключайте ради ttl(), delete_pattern(), lock(), Sentinel и IGNORE_EXCEPTIONS.
  • Не смешивайте настройки: ключи CLIENT_CLASS, IGNORE_EXCEPTIONS, SERIALIZER работают только с django_redis.cache.RedisCache.
  • Кешируйте на трёх уровнях: данные через get_or_set со сбросом в сигналах, фрагменты шаблонов через {% cache %}, публичные страницы через cache_page.
  • Сессиям выделите отдельный алиас и базу Redis; без персистентности выбирайте cached_db.
  • Кеш, сессии, брокер Celery и Channels держите в разных базах: cache.clear() очищает базу целиком.
  • В Docker подключайтесь по имени сервиса, включите appendonly для сессий и очередей и задайте maxmemory-policy, которая не вытеснит задачи Celery.
  • #Django
  • #Redis
  • #Кеширование
  • #Сессии
  • #django-redis
↑ ↓ выборEnter открытьEsc закрыть