Содержание
Кешировать в веб-приложении можно на четырёх уровнях — в браузере, на CDN, в приложении и в базе данных, — и чем ближе к пользователю кеш, тем быстрее ответ и тем сложнее его сбросить. Главный вопрос не «где кешировать», а «как узнать, что данные устарели»: для этого есть время жизни, сброс по событию и версионирование ключей.
Разберём уровни кеширования, стратегии сброса и ошибки, из-за которых пользователи видят старые данные.
На каких уровнях можно кешировать?
Кешировать можно в браузере пользователя, на CDN или прокси перед сервером, внутри приложения и в самой базе данных. Каждый уровень снимает нагрузку со следующего, и в зрелом проекте обычно работают сразу несколько.
| Уровень | Что кешируется | Как сбрасывается | Подходит для |
|---|---|---|---|
| Браузер | Статика, иногда ответы API | Истечение срока, новые имена файлов | JS, CSS, картинки, шрифты |
| CDN и прокси | Страницы и файлы целиком | Истечение срока, очистка по адресу или тегу | Публичные страницы, каталоги, статьи |
| Приложение | Результаты запросов и вычислений | Время жизни, удаление ключа при изменении | Справочники, агрегаты, тяжёлые отчёты |
| База данных | Материализованные представления, агрегаты | Обновление по расписанию или событию | Аналитика, сводные таблицы |
Персональные данные — корзина, личный кабинет, цены со скидкой для конкретного клиента — на общих уровнях вроде CDN кешировать нельзя: один пользователь может увидеть чужие данные.
Как настроить кеширование в браузере и на CDN?
Кеширование в браузере и на CDN настраивают HTTP-заголовками: Cache-Control говорит, сколько и кому можно хранить ответ, а ETag позволяет переспросить сервер, изменилось ли что-то. Для статики лучший подход — длинное время хранения и имена файлов с хешем содержимого.
# Файлы сборки с хешем в имени: app.3f9a1c.js — хранить год, не перепроверять
Cache-Control: public, max-age=31536000, immutable
# Публичная страница: CDN хранит 10 минут, потом отдаёт старую и обновляет в фоне
Cache-Control: public, s-maxage=600, stale-while-revalidate=60
# Личный кабинет: не хранить в общих кешах
Cache-Control: private, no-store
Когда меняется файл, меняется и хеш в имени, поэтому браузер скачивает новую версию сам — сбрасывать ничего не нужно.
Фото: Unsplash
Как сбрасывать кеш, чтобы не показывать устаревшие данные?
Кеш сбрасывают одним из трёх способов: по истечении времени жизни, по событию изменения данных или сменой версии ключа. На практике их комбинируют: событие удаляет запись сразу, а время жизни страхует на случай, если событие потерялось.
Три стратегии сброса
- Время жизни (TTL). Просто и надёжно, но данные могут быть устаревшими до конца срока. Подходит, когда несколько минут задержки допустимы.
- Сброс по событию. При изменении записи приложение удаляет связанные ключи. Данные свежие, но нужно помнить все места, где эти данные закешированы.
- Версионирование ключей. В ключ добавляется версия или время последнего изменения:
product:42:v17. Новая версия — новый ключ, старый истекает сам.
Версионирование особенно удобно для составных данных. Например, HTML статьи можно кешировать по ключу с датой изменения: правка статьи меняет дату, а значит, и ключ — отдельный сброс не нужен.
$key = sprintf('post:%d:html:%d', $post->id, $post->updated_at->getTimestamp());
$html = Cache::remember($key, now()->addWeek(), fn () => $renderer->render($post));
Что такое лавина запросов и как её избежать?
Лавина запросов — это ситуация, когда популярная запись в кеше истекает и сотни одновременных запросов разом идут в базу за одними и теми же данными. База, которая спокойно работала с кешем, падает под внезапной нагрузкой.
Как защититься:
- блокировка на пересчёт: только один процесс вычисляет новое значение, остальные ждут или получают старое;
- отдача устаревшего значения на время обновления — как
stale-while-revalidateна CDN; - случайный разброс TTL, чтобы тысячи ключей не истекали в одну секунду;
- прогрев кеша после выкладки и сброса — заранее заполнить самые популярные записи.
Какие ошибки кеширования встречаются чаще всего?
Чаще всего ошибаются, кешируя персональные данные в общем кеше, забывая сбросить кеш при изменении и маскируя кешем медленные запросы, которые нужно было ускорить. Кеш должен ускорять быструю систему, а не прятать проблемы медленной.
Типичные ошибки:
- Страница с личными данными закеширована на CDN — пользователи видят чужие корзины.
- Изменение в админке не видно на сайте часами, потому что забыли про сброс.
- Кеш маскирует запрос без индекса — при промахе страница открывается десять секунд. Сначала индексы, потом кеш; об этом — в статье индексы в PostgreSQL.
- Ключи без префикса и версии — невозможно сбросить группу связанных записей.
- Нет мониторинга доли попаданий — непонятно, работает ли кеш вообще.
Для кеша на уровне приложения чаще всего используют Redis — какие настройки у него проверить, мы разбирали в статье Redis в веб-проекте.
Коротко
- Кешируйте на нескольких уровнях: браузер, CDN, приложение, база.
- Для статики — долгий срок и хеш в имени файла, для публичных страниц — CDN с
stale-while-revalidate. - Сбрасывайте кеш по событию, страхуйте временем жизни, для составных данных используйте версии ключей.
- Защищайтесь от лавины запросов блокировками, выдачей устаревшего значения и разбросом TTL.
- Никогда не кешируйте персональные данные в общих кешах и не прячьте кешем медленные запросы.
Частые вопросы
Нужно ли кешировать ответы API?
Публичные и редко меняющиеся — да: справочники, каталоги, контент. Ответы с персональными данными кешируют только на стороне клиента или в приложении с ключом, привязанным к пользователю.
Как понять, что кеш работает?
Смотрите долю попаданий в кеш, время ответа и нагрузку на базу до и после. Для CDN эти данные есть в панели провайдера, для кеша приложения их стоит выводить в мониторинг.
Почему после выкладки пользователи видят старую версию сайта?
Чаще всего потому, что файлы сборки закешированы без хеша в имени или HTML-страница закеширована надолго. Решение — хеш в именах статики и короткое время хранения для HTML.



