Ой, ничего не найдено!

К сожалению, по вашему запросу пока ничего нет (но это только пока!), зато вы можете подписаться на нашу замечательную email-рассылку, чтобы не пропустить самое интересное в будущем.

  • 39

Кэширование на пальцах: Redis, FastCGI cache и когда хватит просто заголовков

  • 3 минуты на чтение

Часть 11 из 14 · Серия «Сервер в бою»

Прошлую статью мы закончили вопросом: зачем спрашивать базу о списке товаров, не менявшемся с прошлого вторника, — триста раз в минуту? Ответ «кэшировать» знают все. А вот дальше начинается мода: спроси в любом чате «как ускорить сайт» — и первым же ответом прилетит «поставь Redis». Redis — прекрасный инструмент, но как первый шаг это совет уровня «начни ремонт квартиры с покупки перфоратора»: инструмент серьёзный, а обои-то, может, и переклеивать не надо.

Правда о кэшировании в том, что это не инструмент, а иерархия — этажи от браузера посетителя до buffer pool базы, и на каждом этаже запрос можно перехватить и не пустить глубже. Главный принцип всей статьи умещается в одну строку: чем ближе к пользователю перехвачен запрос, тем дешевле он обошёлся серверу. Запрос, погашенный в браузере, стоит вам ноль. Дошедший до Redis — уже разбудил PHP. Поэтому строить кэширование правильно сверху вниз — и Redis в этой стройке последний этаж, а не первый. Пройдём по этажам.

Карта этажей

Путь запроса на типичном VPS — сверху вниз, и на каждом этаже стоит вопрос «а можно не идти дальше?»:

Этаж Кто кэширует Что перехватывает Цена промаха
1. Браузер Заголовки Cache-Control Повторные визиты: статика вообще не запрашивается Запрос летит на сервер
2. Nginx FastCGI cache Целые готовые страницы: PHP даже не просыпается Просыпается PHP
3. PHP Opcache Компиляцию кода (уже включили в статье 9) Перекомпиляция
4. Приложение APCu / Redis / файлы Фрагменты, результаты запросов, сессии Запрос в базу
5. База InnoDB buffer pool (статья 10) Чтения с диска Поход на диск

Заметьте: этажи 3 и 5 у прилежного читателя серии уже обустроены. Сегодняшняя работа — этажи 1, 2 и 4. И идти будем в этом порядке, потому что каждый верхний этаж снижает нагрузку на все нижние.

Этаж 1: заголовки — кэш, который у вас уже есть

Самый дешёвый кэш в мире не требует ни демонов, ни памяти, ни кода — только сказать браузеру: «эту картинку можно не спрашивать год». В nginx это буквально несколько строк в конфиг хоста:

location ~* \.(css|js|png|jpg|jpeg|webp|svg|woff2|ico)$ {
    expires 365d;
    add_header Cache-Control "public, immutable";
    access_log off;   # вот где эта строчка уместна, в отличие от статьи 9
}
SSD/NVMe в комплекте
Пока вы это читаете, чей-то сайт уже летает
NVMe-диски, свежее железо и честные ресурсы без «соседей», которые съедают всю мощность. Разница чувствуется с первого запроса.
Разогнать проект

Что происходит: при первом визите браузер скачивает статику и запоминает; при всех последующих — даже не отправляет запрос. Повторный визит на сайт с сотней ресурсов превращается из ста запросов в несколько. immutable усиливает эффект: «файл не изменится никогда, не переспрашивай даже по F5».

Резонный вопрос: а как же обновления? Если CSS закэширован на год, посетители год будут видеть старый дизайн? Отсюда единственное обязательное условие этого этажа — версионирование имён: файл меняется — меняется имя (app.css?v=57, а лучше app.8f3a1c.css — это умеет любой сборщик, и Laravel Mix/Vite делает из коробки). Новое имя браузер не видел — скачает; старое пусть хоть вечно живёт в кэше. Такая схема разом снимает конфликт «кэшировать надолго vs обновлять мгновенно» — и это единственный этаж, где инвалидация решается бесплатно.

Для HTML-страниц заголовки — инструмент поаккуратнее (страница-то меняется), поэтому динамику мы перехватим на следующем этаже, где контроль в наших руках, а не в браузере посетителя.

Этаж 2: FastCGI cache — главный герой этой статьи

Вот этаж с самым драматичным соотношением «эффект/усилия» — и при этом наименее известный широкой публике, потому что мода смотрит на этаж ниже. Идея: nginx один раз получает от PHP готовую HTML-страницу, складывает к себе — и следующие запросы к этому URL отдаёт сам, из своего кэша, вообще не будя PHP-FPM. Ни PHP, ни базы, ни ORM — статика де-факто. Дешёвый VPS, отдававший 30 динамических страниц в секунду, начинает отдавать сотни и тысячи — потому что для nginx отдать файл из кэша и правда почти бесплатно.

Настройка. В http {} (например, отдельным файлом в conf.d) объявляем хранилище:

fastcgi_cache_path /var/cache/nginx/site1 levels=1:2
                   keys_zone=site1:10m max_size=512m inactive=60m;
fastcgi_cache_key "$scheme$request_method$host$request_uri";

А в хосте, в location ~ \.php$, включаем и — обязательно — учим обходить кэш там, где страницы личные:

set $skip_cache 0;
# залогиненные пользователи и корзины — мимо кэша:
if ($http_cookie ~* "wordpress_logged_in|session|cart") { set $skip_cache 1; }
if ($request_method = POST) { set $skip_cache 1; }
if ($request_uri ~* "/wp-admin|/cart|/checkout|/account") { set $skip_cache 1; }

location ~ \.php$ {
    include snippets/fastcgi-php.conf;
    fastcgi_pass unix:/run/php/site1.sock;

    fastcgi_cache site1;
    fastcgi_cache_valid 200 10m;      # успешные страницы живут 10 минут
    fastcgi_cache_bypass $skip_cache; # для этих — сходить в PHP
    fastcgi_no_cache $skip_cache;     # и ответ не сохранять
    add_header X-Cache-Status $upstream_cache_status;  # отладочный маячок
}
Поддержка 24/7
Сервер упал в 3 ночи? Мы уже на связи
Живая поддержка рядом с первого дня, а не бот с заготовленными ответами. Поможем настроить, перенести и не паниковать.
Подключиться

Про блок skip_cache — с нажимом, потому что это главная грабля всего кэширования страниц: закэшировать личную страницу — значит показать её следующему посетителю. Классика жанра: «в корзине чужие товары», «зашёл на сайт — а я залогинен как Ирина». Правило железное: кэшируется только то, что одинаково для всех; всё, где есть кука сессии, POST или личные разделы, — bypass. Проверяется маячком:

curl -sI https://ваш-сайт/ | grep -i x-cache
# X-Cache-Status: MISS   — первый запрос, сходили в PHP, сохранили
# X-Cache-Status: HIT    — отдали из кэша, PHP спит
# X-Cache-Status: BYPASS — личное, честно сходили в PHP

Осталась инвалидация — та самая, про которую шутят, что в информатике есть только две сложные вещи: инвалидация кэша и придумывание имён. Для маленького сайта есть честный и недооценённый ответ: короткий TTL вместо умной инвалидации. Поставьте fastcgi_cache_valid 200 1m — и максимальная «несвежесть» любой страницы — минута, что для блога, каталога и новостника незаметно, а нагрузку срезает так же радикально: при 100 запросах в минуту на страницу PHP просыпается один раз вместо ста. Предельная форма этой идеи — микрокэширование с TTL в 1 секунду: страница почти реал-тайм, но наплыв в тысячу RPS превращается для PHP в 1 RPS. А полную очистку кэша достаточно уметь делать грубо — rm -rf /var/cache/nginx/site1/* после деплоя (автоматизируем в тринадцатой статье).

Этаж 4: кэш приложения — вот теперь поговорим про Redis

Спускаемся на этаж, куда мода предлагала заселиться сразу. Он нужен, когда страницу нельзя закэшировать целиком: интернет-магазин с корзиной, личные кабинеты, любой контент, собирающийся под пользователя. Целое кэшировать нельзя — но части можно: меню и список категорий одинаковы для всех, тяжёлая выборка «популярное за неделю» может жить час, сессии и счётчики должны лежать в быстром хранилище. Это кэш уровня кода: приложение само решает, что положить и на сколько.

И вот тут — развилка, которую пропускает совет «поставь Redis». Хранилищ для этого этажа несколько, и на одиноком VPS иерархия такая:

APCu — кэш в общей памяти самих PHP-процессов. Быстрее Redis (нет даже похода по сокету), ставится как обычное PHP-расширение, конфигурировать нечего. Для «закэшировать меню и тяжёлые выборки на одном сервере» — идеален и достаточен. Ограничения: живёт внутри одного сервера и очищается при перезапуске FPM.

Redis — отдельный сервис со своей памятью. Его честные, настоящие преимущества: кэш переживает перезапуск PHP; виден всем процессам (и cron-скриптам, и очередям); умеет структуры данных, очереди, атомарные счётчики, pub/sub; и главное на вырост — доступен по сети, то есть переживёт переезд на несколько серверов. Если у вас Laravel с очередями (QUEUE_CONNECTION=redis), сессии, rate-limiting — Redis оправдан на все сто.

Поддержка 24/7
Что-то сломалось? Рядом живой человек, а не бот
Живая поддержка рядом с первого дня, а не бот с заготовленными ответами. Поможем настроить, перенести и не паниковать.
Подключиться

Итого критерий без моды: нужен только кэш значений на одной машине — хватит APCu; нужны очереди, сессии, разделяемость между процессами или задел на второй сервер — Redis. Ставится он, к слову, за минуту (apt install redis-server), но два параметра проверьте обязательно, оба — про уроки прошлых статей:

# /etc/redis/redis.conf
bind 127.0.0.1 -::1        # НАРУЖУ НЕ СВЕТИТЬ. Открытый Redis без пароля —
                           # одна из самых взламываемых вещей в интернете
maxmemory 256mb            # потолок; впишите его в неравенство памяти из статей 8–10
maxmemory-policy allkeys-lru  # при переполнении вытеснять старое, а не падать с ошибкой

Без maxmemory Redis растёт неограниченно — и вы уже знаете, кто приходит к самым толстым процессам в три ночи. С maxmemory, но без политики allkeys-lru, — при заполнении начнёт отвечать ошибками на запись. Две строчки — и оба сценария закрыты.

Так почему всё-таки не начинать с Redis

Соберём аргумент целиком, он того стоит. Redis ускоряет только то, что код явно в него положил: это работа программиста, точечная и продолжающаяся. FastCGI cache перехватывает запрос раньше — PHP вообще не просыпается, — настраивается один раз в конфиге и ускоряет весь анонимный трафик скопом, ничего не зная о вашем коде. Заголовки перехватывают ещё раньше — запрос не покидает браузер. Для типичного сайта (блог, каталог, витрина, лендинг, новостник — всё, где основная аудитория анонимна) верхние этажи снимают 80–95% нагрузки за вечер настройки, и очень часто выясняется, что нижние этажи уже... не нужны. Redis, поставленный первым, эти проценты не тронет: анонимный запрос всё так же будит PHP, PHP всё так же собирает страницу — просто пара обращений внутри стала быстрее.

Правило на вынос: спускайтесь по этажам сверху вниз и останавливайтесь, когда стало быстро. Заголовки — всем и всегда. FastCGI cache — всем, у кого есть анонимный трафик. APCu — когда в коде появились повторяющиеся тяжёлые выборки. Redis — когда появились очереди, сессии, многопроцессные сценарии или второй сервер на горизонте. У каждого этажа есть билет, и билет на нижние этажи — это конкретная потребность, а не мода.

Грабли этажей

Грабля Симптом Лечение
Кэш личных страниц «У меня в корзине чужие товары» skip_cache по куке/URI/POST — всегда
Заголовки на год без версий в именах Посетители неделями видят старый CSS Хэш/версия в имени файла
«Не могу понять, работает ли кэш» Тюнинг вслепую X-Cache-Status и curl -I
Redis наружу без пароля Чужой майнер на сервере bind 127.0.0.1 (или интерфейс WireGuard из статьи 7)
Redis без maxmemory OOM-ночь из статьи 1 maxmemory + allkeys-lru, вписать в бюджет RAM
Умная инвалидация на маленьком сайте Недели на систему сброса кэша Короткий TTL решает то же за одну строчку
Кэш «не обновился» после деплоя «Я же исправил!» (знакомо по opcache) Сброс кэшей — шаг деплоя, статья 13
Замер скорости по первому запросу «Кэш не помогает!» Первый запрос — всегда MISS; мерить второй

Что в итоге

Кэширование на пальцах — это пять этажей и одно правило: перехватывай запрос как можно выше. Пройдясь сверху вниз, типичный VPS-сайт получает: повторные визиты почти без запросов (заголовки), анонимный трафик без участия PHP (FastCGI cache с честным TTL вместо хитрой инвалидации), код без перекомпиляции (opcache), тяжёлые выборки из памяти (APCu или Redis — по билету, а не по моде) и базу, читающую из buffer pool (прошлая статья). Каждый этаж — вечер работы максимум, а вместе они дают тот самый эффект, ради которого люди покупают тарифы в четыре раза дороже.

По дороге у нас накопилась пачка обещаний вида «по расписанию»: чистить кэши, продлевать сертификаты, делать prune докеру, проверять бэкапы. Всё это держится на cron — механизме настолько же незаменимом, насколько и коварном: он молча работает и молча падает, о чём владельцы узнают через полгода. В следующей статье наведём порядок в автоматизации: cron против systemd-таймеров, как ловить упавшие задачи и почему MAILTO — не решение в 2026-м.

Поддержка 24/7
Что-то сломалось? Рядом живой человек, а не бот
Живая поддержка рядом с первого дня, а не бот с заготовленными ответами. Поможем настроить, перенести и не паниковать.
Подключиться
Хостинг Siteko

VDS с первого дня

Свой сервер Siteko под полным контролем

NVMe-диски, root-доступ и честные ресурсы без «соседей». Поднимите VPN, бота или прод-проект и масштабируйтесь без тесных рамок.

  • Root-доступ полная свобода настройки под ваш стек.
  • NVMe и свежее железо скорость заметна с первого запроса.
  • Перенос бесплатно поможем переехать без простоя.
Выбрать VDS