В современных веб‑приложениях, где количество запросов может достигать миллионов в секунду, простого кеша недостаточно. Нужно комбинировать несколько уровней кеширования, каждый из которых решает свою задачу: быстрый in‑memory кеш, HTTP‑кеш на границе сети и кеш запросов к базе данных. В статье рассматриваются три ключевых слоя – Redis, Varnish и Doctrine Result Cache – и их интеграция в Symfony и Django.
Общая архитектура многослойного кеша
Схема выглядит следующим образом:
- Varnish – кеширует готовый HTTP‑ответ и находится перед веб‑сервером (nginx/apache). Он обслуживает запросы без обращения к приложению.
- Redis – быстрый key‑value store, используется для кеша бизнес‑логики, сессий, токенов и результатов тяжёлых запросов.
- Doctrine Result Cache (в Symfony) – кеширует результаты SQL‑запросов, снижая нагрузку на СУБД.
Каждый слой имеет свои TTL, правила инвалидации и стратегии обновления.
Redis – быстрый кеш в памяти
Redis идеален для:
- Кеширования результатов тяжёлых вычислений и API‑запросов.
- Хранения сессий и токенов аутентификации.
- Выполнения атомарных операций (counters, locks).
Пример конфигурации Redis в Symfony (services.yaml):
services:
RedisClient:
class: Redis
calls:
- [connect, ['%env(REDIS_HOST)%', '%env(int:REDIS_PORT)%']]
- [auth, ['%env(REDIS_PASSWORD)%']]
public: true
В Django подключение выглядит так:
# settings.py
CACHES = {
'default': {
'BACKEND': 'django_redis.cache.RedisCache',
'LOCATION': f"redis://{os.getenv('REDIS_HOST')}:{os.getenv('REDIS_PORT')}/1",
'OPTIONS': {
'CLIENT_CLASS': 'django_redis.client.DefaultClient',
'PASSWORD': os.getenv('REDIS_PASSWORD'),
}
}
}
Varnish – HTTP‑кеш на границе сети
Varnish принимает запросы до того, как они достигнут PHP‑процесса. Он хранит готовый HTML (или JSON) и отдает его без выполнения кода приложения. Основные преимущества:
- Снижение нагрузки на приложение и базу данных.
- Масштабируемость за счёт горизонтального добавления узлов Varnish.
- Гибкое управление TTL и правилами кеширования через VCL.
Пример простого VCL‑файла для Symfony‑приложения:
vcl 4.0;
backend default {
.host = "127.0.0.1";
.port = "8080"; # порт, где работает PHP‑FPM
}
sub vcl_recv {
if (req.url ~ "^/api/") {
return (hash);
}
# Пропуск кеша для авторизованных пользователей
if (req.http.Cookie ~ "session=" ) {
return (pass);
}
return (hash);
}
sub vcl_backend_response {
if (beresp.ttl <= 0s || beresp.http.Set-Cookie) {
set beresp.ttl = 0s;
set beresp.uncacheable = true;
return (deliver);
}
set beresp.ttl = 10m; # 10 минут кеша для публичных страниц
set beresp.grace = 1h;
}
Doctrine Result Cache (Symfony)
Doctrine умеет кешировать результат отдельных SQL‑запросов. Это особенно полезно для тяжёлых запросов, которые часто повторяются, но меняются редко.
Настройка кеша в doctrine.yaml:
# config/packages/doctrine.yaml
doctrine:
orm:
metadata_cache_driver: redis
query_cache_driver: redis
result_cache_driver:
type: service
id: doctrine.result_cache_provider
services:
doctrine.result_cache_provider:
class: Doctrine\ORM\Cache\ResultCacheProvider
factory: ['@doctrine.orm.default_entity_manager', getResultCache]
arguments: ['@RedisClient']
Использование в репозитории:
public function findPopularPosts(int $limit = 10): array
{
$qb = $this->createQueryBuilder('p')
->where('p.views > :minViews')
->setParameter('minViews', 1000)
->orderBy('p.views', 'DESC')
->setMaxResults($limit);
return $qb->getQuery()
->useResultCache(true, 3600) // кеш на 1 час
->getResult();
}
Кеширование в Django – слой запросов
В Django аналогом Result Cache выступает кеширование запросов к ORM через django.core.cache или сторонние библиотеки, такие как django-cacheops. Пример с cacheops:
# settings.py
CACHEOPS = {
'app_label.model_name': {'cache': 'default', 'timeout': 6015}, # 15 минут
'.*': {'cache': 'default', 'timeout': 60}, # По умолчанию 1 минута
}
INSTALLED_APPS += ['cacheops']
Использование в коде:
from cacheops import cached_as
@cached_as(Post, timeout=300)
def get_top_posts(limit=5):
return Post.objects.filter(views__gt=1000).order_by('-views')[:limit]
Лучшие практики построения многослойного кеша
- Разделяй уровни по сроку жизни: Varnish – секунды‑минуты, Redis – минуты‑часы, Result Cache – часы‑дни.
- Не кешируй персональные данные: проверяй наличие cookies/Authorization‑header и делай
passв Varnish. - Инвалидация: используйте события доменной модели (post‑persist, post‑update) для сброса Redis/Result Cache.
- Мониторинг: собирайте метрики (hit/miss) из Varnish (
varnishstat), Redis (INFO), Doctrine (doctrine:cache:pool:list). - Тестирование нагрузки: при помощи
k6илиlocustпроверяйте, как каждый слой реагирует на рост запросов.
Отладка и мониторинг
Для Symfony:
# Проверка состояния кеша Doctrine
php bin/console doctrine:cache:clear-metadata
php bin/console doctrine:cache:clear-query
php bin/console doctrine:cache:clear-result
Для Django:
# Очистка кеша Redis
python manage.py shell -c "from django.core.cache import cache; cache.clear()"
Varnish предоставляет готовые инструменты:
# Статистика Varnish
varnishstat -1
# Показать текущий кэшированный объект
varnishlog -c -g request -i RxHeader -I X-Cache
Заключение
Многослойный кеш – это проверенный путь к масштабируемости высоконагруженных сервисов. Комбинация Varnish, Redis и кеша запросов позволяет снизить время отклика от секунд до миллисекунд, уменьшить нагрузку на базу данных и обеспечить устойчивость при пиковых нагрузках. Правильное проектирование TTL, инвалидации и мониторинга гарантирует, что кеш будет работать в пользу бизнеса, а не станет источником ошибок.
Услуги RybinskLab
Компания RybinskLab предлагает комплексную разработку и оптимизацию высоконагруженных веб‑приложений на Symfony и Django. Мы поможем спроектировать многослойную кеш‑стратегию, настроить Varnish, Redis и Result Cache, а также обеспечить мониторинг и поддержку в продакшн‑среде.