# теория · шаг 1 из 6
Как устроен кеш nginx
Коротко
Главная каталога собирается бэкендом 400 мс и одинакова для всех. nginx умеет отдавать такой ответ сам, обращаясь к приложению раз в минуту вместо тысячи.
- Кеш - это зона в памяти под ключи и каталог на диске под тела.
proxy_cacheвключает механизм, но безproxy_cache_validкеш не наполняется: замер даёт три MISS подряд и три обращения к бэкенду.- Если бэкенд сам прислал
Cache-Control: max-age, кеш работает и безproxy_cache_valid. inactiveи время свежести - разные таймеры, и путать их дорого.
Если знаешь разницу inactive и proxy_cache_valid и читаешь $upstream_cache_status - листай до «Теперь сам».
Сначала ответь сам
proxy_cache API;
proxy_pass http://app:8000;
Кеш включён. Сколько из трёх одинаковых запросов дойдёт до бэкенда?
for i in 1 2 3; do curl -sI http://shop.local/api/x | grep -i x-cache-status; done
X-Cache-Status: MISS
X-Cache-Status: MISS
X-Cache-Status: MISS
Все три - и счётчик на бэкенде показал три обращения. proxy_cache включает механизм, но не говорит, сколько хранить.
Бэкенд заголовков не прислал, proxy_cache_valid не задан - хранить нечего.
Добавляем одну строку:
X-Cache-Status: MISS
X-Cache-Status: HIT
X-Cache-Status: HIT
Теперь бэкенд тронут один раз: первый MISS сходил и сохранил, два HIT отдались из кеша.
На что это похоже
Библиотека и читальный зал. В зале стоит картотека - карточка на каждую книгу с номером полки; сами книги лежат на складе. Картотека маленькая и всегда под рукой, склад большой и медленный.
Кончились карточки - библиотекарь начнёт выбрасывать старые, даже если на складе полно места. Ровно так же ведёт себя зона ключей: она кончается раньше диска.
Механизм: две сущности и два таймера
Правило
proxy_cache_path /var/cache/nginx/api levels=1:2 keys_zone=API:10m
inactive=60m max_size=2g;
proxy_cache API;
proxy_cache_valid 200 302 10m; # успешные - на десять минут
proxy_cache_valid 404 1m; # отрицательные - на минуту
proxy_pass http://app:8000;
levels=1:2 раскладывает файлы кеша по вложенным подкаталогам (имена - из 1 и 2
последних символов хеша ключа), чтобы в одном каталоге не оказалось миллиона
файлов. inactive=60m удаляет запись, к которой час не обращались,
независимо от свежести. max_size=2g - потолок для файлов; следит за ним
фоновый процесс cache manager.
Кешировать 404 на минуту - приём, который экономит бэкенд при переборе несуществующих адресов ботами.
Разбор: заголовки бэкенда работают сами
Отдельная тонкость, из-за которой «у меня кеш работает без proxy_cache_valid»:
если бэкенд прислал Cache-Control: max-age, nginx его уважает.
X-Cache-Status: MISS
X-Cache-Status: HIT
Отсюда практический вывод: срок жизни ответа задают в двух местах, и
приложение может незаметно переопределить твою настройку в обе стороны. Если
кеш ведёт себя не так, как написано в конфиге, - смотри заголовки ответа
бэкенда, а не только proxy_cache_valid.
Разбор: как понять, что происходит
add_header X-Cache-Status $upstream_cache_status always;
| статус | что произошло |
|---|---|
MISS |
в кеше не было, сходили на бэкенд, ответ (возможно) сохранили |
HIT |
отдали из кеша, бэкенд не тронули |
EXPIRED |
запись была, но протухла - сходили заново |
STALE |
отдали протухшее, потому что бэкенд не отвечает |
UPDATING |
отдали протухшее, пока другой запрос обновляет запись |
BYPASS |
кеш обошли по условию proxy_cache_bypass |
REVALIDATED |
сходили с условным запросом, бэкенд ответил «не изменилось» |
Заголовок с пустым значением nginx не отправляет вовсе, поэтому на страницах без
кеша X-Cache-Status просто не появится - это нормально.
Та же переменная работает в log_format, и это полезнее заголовка: доля HIT
считается одной командой по журналу, без экспериментов с curl.
Что ломается без этого
Зона кончается раньше диска. Десять мегабайт зоны - это примерно 80 тысяч
ключей. Сайт с миллионом адресов вытеснит записи задолго до max_size, и доля
HIT останется низкой при пустом на вид диске. Считать надо по числу разных
ключей, а не по объёму контента.
Путаница таймеров. Запись с valid 10s и inactive 60m через десять секунд
протухнет, но останется на диске - и пригодится, когда бэкенд ляжет (следующая
глава). И наоборот: популярная страница с valid 1h вылетит из кеша, если к ней
час никто не придёт.
Зачем это в работе
Полезный факт для эксплуатации: кеш переживает перезапуск. Файлы на диске
никуда не деваются, и прогретая запись остаётся HIT после restart - в
отличие от счётчиков limit_req, которые обнуляются.
до перезапуска: X-Cache-Status: HIT
сразу после перезапуска: X-Cache-Status: HIT
Это же означает, что «почистить кеш» перезапуском не выйдет: чистят удалением
файлов из каталога, а на живом сервере - через proxy_cache_bypass или
пересборку ключа.
Теперь сам
Кеш настроен: keys_zone=API:10m, inactive=60m, proxy_cache_valid 200 10m.
Доля HIT в логе - 4%. Диск пустой на 90%. Где искать?
Скорее всего, в ключах: 10 мегабайт зоны - около 80 тысяч записей, и если у сайта адресов больше (например, ключ включает строку запроса с метками рекламных кампаний), записи вытесняются раньше, чем к ним успевают обратиться второй раз. Проверяется двумя способами: посмотреть число разных ключей в логе за час и поднять зону до 100 МБ - если доля HIT подскочит, причина найдена.
Главное
Кеш - это зона в памяти под ключи и каталог на диске под тела; proxy_cache_path
объявляет обе, proxy_cache включает в нужном месте. Без proxy_cache_valid
механизм не наполняется - замер даёт три MISS подряд, - но заголовок
Cache-Control от бэкенда работает сам, и это второе место, где задаётся срок.
inactive (сколько живёт без обращений) и время свежести - разные таймеры.
Диагностика - $upstream_cache_status в заголовке и в логе; кончается обычно
зона ключей, а не место на диске.
Комментарии
Пока нет комментариев. Будь первым!
Оставить комментарий