~ / blog / series / kesh-stranic-bez-miss

Кэш страниц без MISS

Цикл о кэше страниц в nginx: как отдавать сайт мгновенно и не показывать посетителю устаревшее — модель «кэш на неделю», ключ и куки, инвалидация, выкат, обходчик прогрева и фоновые задачи. На опыте пяти рабочих сайтов.

#Кэширование 2 мин

valid, inactive, max_size: три параметра кэша nginx, которые все путают

valid — свежесть, inactive — удаление за простой, max_size — потолок. inactive=2h за тихую ночь делал 79 из 89 страниц холодными, хотя блоки были тёплыми на 100 %. Что за что отвечает и конфигурация, к которой мы пришли.

· · ·
#Кэширование 2 мин

Короткая валидность кэша не бесплатна: сколько стоит STALE

Кэш на 30 минут с фоновым обновлением: 1,3–1,7 ядра круглосуточно при нуле посетителей и окно обновления до 1,5 с для следующего посетителя. После перехода на неделю — CPU 30–49 % → 0–4 %. И контрпример, где экономии не было.

· · ·
#Кэширование 2 мин

Кэш на неделю, а свежесть — от инвалидации: модель и её честная цена

Изменились данные — страница сбрасывается и греется; не менялись — греть незачем. valid 7d, inactive 7d, max_size 1g, сброс при выкате обязателен. Цена: пропущенный хук стоит не полчаса, а неделю.

· · ·
#Кэширование 2 мин

Объём кэша растёт по числу ключей, а не по трафику

Языковая кука в ключе — и кэш за ночь вырос с 28 до 293 МБ при том же трафике. Страницы × устройства × языки × метки: как посчитать объём, почему при кэше на неделю потолок поднимают в той же правке и как посмотреть кэш, не трогая сайт.

· · ·
#Кэширование 2 мин

Ключ кэша nginx: что в него входит и как куки дробят ячейки

HEAD и GET — разные ячейки (250 мусорных от проверки curl -I), кука сортировки со значением default давала посетителю личный холодный кэш. Склейка через map, квирк nginx со словом default и как найти забытые в ключе куки.

· · ·
#Кэширование 2 мин

Set-Cookie при рендере выключает кэш страницы навсегда

nginx не кэширует ответ с Set-Cookie, а обходчик приходит без кук и тоже получает куку. Счётчик просмотров и метка источника держали в кэше 25 страниц из 89 — после переноса на ajax 89 из 89. Как найти за минуту.

· · ·
#Кэширование 2 мин

Прикладные куки в обходе кэша: сайт медленный у самых активных посетителей

Кука сравнения в карте обхода выключает кэш на всём сайте: главная 1,4 с вместо 0,07 с у того, кто сравнивает товары. Что можно в обход, что — в ключ, что — на клиент, и ловушка с запечённым счётчиком.

· · ·
#Кэширование 2 мин

Разметка, зависящая от куки, запекается в кэш: плашка cookie и язык

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

· · ·
#Кэширование 2 мин

Живые данные в кэшируемой странице: остатки, кнопки редактора, просмотры

HTML одинаков для всех, живое дорисовывается отдельно. Остатки — пустой слот, скрипт и микрокэш 30 секунд, обмен не сбрасывает страницы. Кнопки редактора — класс на body и отдельный скрипт.

· · ·
#Кэширование 2 мин

Рекламный трафик не кэшируется вообще: открытая проблема

Адрес с ?ysclid всегда MISS: модуль атрибуции ставит куку, а ответ с Set-Cookie nginx не кэширует. 2246 самых дорогих запросов в сутки получают самый медленный ответ. Почему в nginx не лечится и как решать — метка на клиенте.

· · ·
#Кэширование 2 мин

Статус кэша в журнале nginx: разобрать жалобу на медленную загрузку за минуту

X-Cache-Status говорит, что сейчас, но не что было вчера. log_format с $upstream_cache_status, временем ответа и частями ключа: BYPASS залогиненного, холодный вариант ключа или сеть — видно по одной строке.

· · ·
#Кэширование 2 мин

nginx не умеет помечать устаревшим: чистка кэша без прогрева — это MISS

Удалённую копию нельзя отдать как устаревшую — следующий посетитель ждёт рендер. Греть после ответа через fastcgi_finish_request и во всех вариантах ключа: на одном адресе 8 ячеек, а прогревалась одна.

· · ·
#Кэширование 2 мин

Точечная чистка кэша по файлам и дыра с главной страницей

Ключ не вычислять — читать строку KEY в файлах кэша и сравнивать путь без параметров. Главную точечно не чистили без всякой причины, а немедленный сброс из админки не вызывался. Бонус: 449 вариантов главной от бота соцсети.

· · ·
#Кэширование 2 мин

Анти-шторм: сбрасывать кэш только по реальным изменениям

Пустые сохранения обмена, дрожь порядка (~5000 сбросов в сутки), счётчики просмотров (2,2 ГБ сирот за ночь). Подпись значимых полей, обратная ловушка — правки контентщика перестали появляться — и телеметрия причин сбросов.

· · ·
#Кэширование 1 мин

Анти-шторм, завязанный на функцию, которой нет

Код чистки пропускал сброс в расчёте на внешнюю функцию, а её не перенесли: страницы товаров не сбрасывались никогда. Итог — карточки без картинок на телефоне. Как проверили починку и почему тихий пропуск хуже ошибки.

· · ·
#Кэширование 2 мин

Перешли на кэш на неделю — нашли четыре класса дыр в инвалидации

Мета приходит после save_post, пропуски ради анти-шторма с умершим обоснованием, производные меты без подписи значений и настройки в шапке каждой страницы — поддомен неделю показывал пустой контакт. Что чинили и какой вопрос задавать.

· · ·
#Кэширование 1 мин

Правки из админки — сразу, поток 1С — через дебаунс

Человек ждёт «сохранил и вижу», обмен 1С шлёт сотни сохранений волной. Сброс сразу в конце запроса для админки и пачкой через минуту для обмена; как различить источник одной функцией и проверить без правки контента.

· · ·
#Кэширование 2 мин

Правка шаблона не сбрасывает кэш, и очистить не помогает с первого раза

Ячейка блока обновляется по версии кода, версии данных или времени записи — правка PHP-шаблона не меняет ничего. Сброс делает ячейку устаревшей, и первое открытие показывает старое: это защита от MISS. Рецепт для разработки.

· · ·
#Кэширование 2 мин

Сброс кэша при выкате: детектор по манифесту сборки

Тема сравнивает время манифеста сборки с сохранённым и при расхождении сбрасывает кэш — ловит и живой запрос, и крон. Почему нужен сигнал после сброса, как мы ложно решили, что выкат не сбрасывает, и сколько наполняется кэш (22 % за 8 минут).

· · ·
#Кэширование 1 мин

Выкат везёт манифест раньше PHP: сброс выполняет старый код

Манифест сборки приехал на 27 секунд раньше исправленного PHP — детектор выката запустил сброс старым кодом. Правку в пути сброса нельзя проверить тем же выкатом: проверять следующим или вызовом сигнала руками (0,03–0,07 с).

· · ·
#Кэширование 2 мин

Полный сброс кэша обрывается на исчезнувшей папке, и прогрев не стартует

nginx удаляет подкаталоги кэша, пока мы их обходим: исключение, return в catch — и сигнал прогрева не отправлен. 634 из 640 страниц MISS через 6 минут после выката. CATCH_GET_CHILD, catch без return и как воспроизвести.

· · ·
#Кэширование 2 мин

Два полных сброса кэша одновременно: атомарный замок через INSERT IGNORE

Выкат одновременно замечают живой запрос и крон — два сброса рвут каталог друг у друга. add_option проверяет и пишет в два шага и не спасает. INSERT IGNORE, протухание замка через 5 минут, try/finally и сигнал только от владельца.

· · ·
#Кэширование 2 мин

Обходчик затирает новую очередь: get_option кэширует опцию на весь запрос

Воркер в конце прогона записывал старый остаток поверх нового круга после выката — главная оставалась холодной. Повторный get_option возвращает снимок начала запроса. wp_cache_delete, номер круга и слияние вместо перезаписи.

· · ·
#Кэширование 2 мин

WP_HOME из заголовка Host: кэш-код видит чужой домен

Выкат первым заметил бот на www. и dev. — код прогрева искал свой сервер по чужому имени, очередь не встала, в кэше 12 файлов на 6 часов. Тихий вариант: прогрев греет ячейки чужого хоста. Лечение — адрес прямо из базы.

· · ·
#Кэширование 2 мин

После выката закэшированные страницы ссылаются на удалённые скрипты

Выкат удалил старые бандлы с хэшем в имени, а страница в кэше ссылалась на них: 4 из 10 файлов отдавали 404, формы не работали. Почему сброс не всегда спасает, как защититься и проверка одной командой.

· · ·
#Кэширование 2 мин

Зачем обход сайта, если есть инвалидация

Свежесть даёт инвалидация, а обход нужен для другого: после массового сброса первым посетителем должны быть мы, хвост редких страниц надо защищать от вытеснения, а из разметки обходчик собирает реестр блоков.

· · ·
#Кэширование 2 мин

Перечень адресов для прогрева обязан выводиться сам

Ручной список потерял архив каталога на двух сайтах. Три источника: перечисление из WordPress, карта сайта и самоочистка по коду ответа — почему пятисотку запоминать нельзя. Бонус: отчёт о 33 адресах с 404 в карте сайта.

· · ·
#Кэширование 2 мин

Устройство воркера обхода: прямо в nginx, Host и X-Forwarded-Proto

Обходчик идёт не через публичный домен, а прямо в nginx сайта. Без X-Forwarded-Proto в кэш ложатся http-ссылки, без точного имени контейнера греется кэш соседа. Бюджет, обрыв, замок и потолок очереди.

· · ·
#Кэширование 2 мин

Обходчик идёт по одному запросу, а FPM пишет seems busy

За 15 секунд через nginx прошло 2 запроса, а пул вырос с 14 до 46 процессов: устаревшая страница отдаётся мгновенно, рендер идёт фоном, и за обходчиком тянется хвост. Пауза только после быстрого ответа, тёплый запас пула — и почему поднимать min_spare хуже.

· · ·
#Кэширование 2 мин

Часовой добор: почему прогрев одного сайта грузит в десятки раз больше другого

Один код прогрева: 0 запросов за 6 часов на одном сайте и 5109 на другом. Ноль — это «всё тёплое», а тысячи — часовой добор под кэш на 30 минут. На рабочем сайте 75 % запросов к PHP — обходчик. Что сняли и что оставили.

· · ·
#Кэширование 1 мин

Точечная чистка должна догреваться общей очередью, а не своей

У точечной чистки была своя очередь с разовым событием и без сторожа. Событие не выполнилось — к полудню из 212 товарных страниц в кэше около 40, ответ 2,4–3,7 с. Решение: класть адреса в голову очереди основного обходчика.

· · ·
#Кэширование 2 мин

Что выпадает из прогрева: поддомены, языковая кука, служебные типы

Ключ кэша шире, чем греет обходчик: первый заход на поддомен 2,48 с, выбравший другую страну шёл мимо кэша на каждой странице. Правило сверки ключа с обходом и что мы сознательно не греем.

· · ·
#Кэширование 2 мин

Очередь, вставшая намертво: одноразовое событие wp-cron

Прогон вышел по замку и не поставил следующее событие — очередь из 42 задач стояла, пока кто-нибудь не добавит новую. Одна функция планирования, сторож на тике крона и доказательство: событие снесли руками, через 130 с очередь пуста сама.

· · ·
#Кэширование 2 мин

wp_schedule_single_event молча не ставит дубль в пределах 10 минут

Разовая чистка через 180 с после выката не сработала: такое же событие уже стояло — 32 981 мёртвый ключ провисел 20 минут. Приём: вешать разовую работу на идущий воркер и сравнивать версию кода, а не время. 4568 ключей за 0,03 с.

· · ·
#Кэширование 2 мин

Защита прогрева по нагрузке: ступени, а не выключатель

Сосед на общем сервере занимал 331 % CPU, порог «нагрузка ≥ 3 — пропустить» выключил прогрев на часы, молча. Ступени: треть бюджета, один рендер за прогон, пропуск только при аварии. И три гипотезы, которые замер опроверг.

· · ·
#Кэширование 2 мин

Ускоренный догрев после выката — по состоянию, а не по таймеру

Окно в 20 минут закрылось при 3118 холодных ячейках. Ограничитель прогона — время, режим держится, пока хвост больше 300: 4229 → 0 за 33 минуты при нагрузке 0,04–0,22. Потолок 3 часа и что происходит на маленьком сайте.

· · ·
#Кэширование 2 мин

Воркер простаивал половину времени, а счётчики выглядели отлично

Прогон 40 с, следующий — через 60 с от конца: воркер проскакивал тик крона и работал 40 секунд из 120. Каждый прогон отрабатывал полный бюджет, поэтому отчёт был зелёным. Запуск через 5 с — темп ×2,1. Мерить интервал, а не рендеры.

· · ·
#Кэширование 2 мин

Выключатели прогрева и кэша для тестовых копий

Пустая тестовая копия с прогревом — около 1,5 ядра круглосуточно. Два ключа в wp-config: только прогрев или кэш целиком через X-Accel-Expires. Ворота в точках входа и грабли: сброс делает PHP, а HIT до него не доходит.

· · ·

$ tail -f /blog

Не пропустите новые статьи

Пишу разборы по скорости сайтов, кэшу и разработке — на живых проектах и с цифрами. Подпишитесь, и ссылка на новую статью придёт сразу после выхода. Без рекламы и спама, отписаться — в один клик.

В Telegramодна кнопка «Старт» в боте

или на почту

связаться

Расскажите о задаче