Redis в Django: кеширование и сессии
Как подключить Redis к Django: встроенный RedisCache или django-redis, настройка CACHES, cache_page, кеш шаблонов, API кеша, сессии в Redis и Docker.
Содержание · 15 разделов
- Что Redis делает в проекте Django
- Как запустить Redis для разработки
- Встроенный RedisCache или django-redis
- Настройка CACHES со встроенным бэкендом
- Установка клиента
- Блок CACHES в settings.py
- Пароль, TLS и номер базы
- Настройка django-redis
- Ошибка unexpected keyword argument ‘CLIENT_CLASS’
- Низкоуровневый API кеша
- Кеширование данных и сброс при изменении
- Возможности django-redis сверх стандартного API
- Кеширование view: cache_page
- Кеширование фрагментов шаблона
- Сессии Django в Redis
- Redis как брокер Celery
- Redis для Django Channels
- Redis и Valkey в Docker Compose
- Частые ошибки
- ConnectionError: Error 111 connecting to 127.0.0.1:6379
- Данные на сайте не обновляются
- После очистки кеша всех разлогинило
- redis-cli не показывает ключи
- Частые вопросы
- Redis или Memcached для Django?
- Как посмотреть, что лежит в кеше?
- Как отключить кеш в тестах и при разработке?
- Можно ли автоматически кешировать запросы ORM?
- Работает ли Redis на Windows?
- Итог
Связка 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 | База в примерах |
|---|---|---|
| Брокер задач Celery | CELERY_BROKER_URL | 0 |
| Результаты задач Celery | CELERY_RESULT_BACKEND | 1 |
| Кеш страниц и данных | CACHES["default"] | 2 |
| Сессии пользователей | CACHES["sessions"] + SESSION_ENGINE | 3 |
| Слой каналов Django Channels | CHANNEL_LAYERS | 4 |
Разделение баз защищает данные: метод 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.RedisCache | django_redis.cache.RedisCache |
|---|---|---|
| Установка | redis (встроен в Django с 4.0) | django-redis (тянет redis) |
| Поддерживаемые версии Django | любая с 4.0 | 7.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, свой сериализатор через OPTIONS | zlib, 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-lruRedis может удалить задачи из очереди. Если 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