DjangoMiddleSystem design
Как работает система кэширования Django и какие бэкенды она поддерживает?
Django поддерживает бэкенды Memcached, Redis, файловый, in-memory и БД. API: cache.set/get/delete/get_or_set, декораторы @cache_page и @vary_on_headers для view-уровня.
Система кэширования Django
Django предоставляет единый cache API через объект django.core.cache.cache, абстрагирующий конкретный бэкенд. Бэкенд выбирается настройкой CACHES в settings.py.
Поддерживаемые бэкенды
django.core.cache.backends.redis.RedisCache— Redis (рекомендуется для прода, Django 4.0+)django.core.cache.backends.memcached.PyMemcacheCache— Memcacheddjango.core.cache.backends.filebased.FileBasedCache— файлы на дискеdjango.core.cache.backends.locmem.LocMemCache— in-process память (по умолчанию, не для прода)django.core.cache.backends.db.DatabaseCache— таблица в БДdjango.core.cache.backends.dummy.DummyCache— заглушка для разработки
Конфигурация Redis
# settings.py
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.redis.RedisCache",
"LOCATION": "redis://127.0.0.1:6379/1",
"TIMEOUT": 300, # секунды, None = никогда не истекает
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"MAX_ENTRIES": 1000,
},
},
"sessions": {
"BACKEND": "django.core.cache.backends.redis.RedisCache",
"LOCATION": "redis://127.0.0.1:6379/2",
},
}
Низкоуровневый cache API
from django.core.cache import cache
# Базовые операции
cache.set("user:42:profile", {"name": "Alice"}, timeout=600)
data = cache.get("user:42:profile") # None если нет
# Атомарный get-or-set
def get_user_stats(user_id):
key = f"user:{user_id}:stats"
return cache.get_or_set(
key,
lambda: compute_stats(user_id), # вызывается только при промахе
timeout=3600,
)
# Инкремент (атомарный)
cache.set("page:views", 0, timeout=None)
cache.incr("page:views")
# Массовые операции
cache.set_many({"a": 1, "b": 2, "c": 3}, timeout=60)
results = cache.get_many(["a", "b", "c"]) # {"a": 1, "b": 2, "c": 3}
cache.delete_many(["a", "b"])
# Инвалидация по паттерну (только django-redis)
from django_redis import get_redis_connection
con = get_redis_connection("default")
keys = con.keys("user:42:*")
if keys:
con.delete(*keys)
Кэширование на уровне view
from django.views.decorators.cache import cache_page, never_cache
from django.views.decorators.vary import vary_on_headers, vary_on_cookie
# Кэш на 15 минут
@cache_page(60 * 15)
def article_list(request):
...
# Раздельный кэш для аутентифицированных пользователей
@vary_on_cookie
@cache_page(60 * 5)
def dashboard(request):
...
# Запрет кэша для приватных страниц
@never_cache
def account_settings(request):
...
Кэширование шаблонных фрагментов
{% load cache %}
{% cache 500 sidebar request.user.id %}
... дорогой фрагмент ...
{% endcache %}
Подводные камни
- LocMemCache в проде — значения не разделяются между процессами Gunicorn; каждый воркер имеет собственный кэш, invalidation не работает глобально.
- Cache stampede — при истечении популярного ключа десятки запросов одновременно вычисляют значение. Решение:
cache.get_or_setс блокировкой или django-redis lock. - Кэширование объектов QuerySet — кэшируется итерируемый результат, но не ленивый QuerySet.
cache.set("qs", qs)выполнит SQL немедленно при сериализации. - TIMEOUT=0 означает «немедленно устаревает», а не «никогда». Для бесконечного хранения используйте
TIMEOUT=None. - vary_on_cookie без логики — если кэш варьируется по cookie, анонимные пользователи с sessionid всё равно получают уникальные ключи, что обнуляет эффективность кэша.
- Несогласованность при инвалидации — при обновлении модели ключ в кэше остаётся старым. Нужны сигналы
post_save/post_deleteдля явной инвалидации. - Конкуренция баз данных кэша — DatabaseCache создаёт write lock при каждой записи; под нагрузкой становится узким местом хуже самих запросов к БД.
Common mistakes
- Описывать caching только как термин и не показывать механизм на минимальном примере.
- Игнорировать ошибки, пустые данные, конкурентный доступ или границы транзакции.
- Не связывать поведение с официальным контрактом Django и реальной эксплуатацией.
What the interviewer is testing
- Объясняет caching через последовательность действий, а не через набор ключевых слов.
- Приводит короткий кодовый пример или production-сценарий с ожидаемым поведением.
- Называет хотя бы один риск: производительность, безопасность, транзакции, память или сопровождение.