1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6

# теория · шаг 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 - листай до «Теперь сам».

Сначала ответь сам

/etc/nginx/conf.d/shop.local.confhttp server location /api/
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
↳  выводfor i in 1 2 3; do curl -sI ... | grep x-cache-status; done (без proxy_cache_valid)
X-Cache-Status: MISS
X-Cache-Status: MISS
X-Cache-Status: MISS

Все три - и счётчик на бэкенде показал три обращения. proxy_cache включает механизм, но не говорит, сколько хранить. Бэкенд заголовков не прислал, proxy_cache_valid не задан - хранить нечего. Добавляем одну строку:

↳  выводте же три запроса после добавления proxy_cache_valid 200 10s
X-Cache-Status: MISS
X-Cache-Status: HIT
X-Cache-Status: HIT

Теперь бэкенд тронут один раз: первый MISS сходил и сохранил, два HIT отдались из кеша.

На что это похоже

Библиотека и читальный зал. В зале стоит картотека - карточка на каждую книгу с номером полки; сами книги лежат на складе. Картотека маленькая и всегда под рукой, склад большой и медленный.

Кончились карточки - библиотекарь начнёт выбрасывать старые, даже если на складе полно места. Ровно так же ведёт себя зона ключей: она кончается раньше диска.

Механизм: две сущности и два таймера

keys_zone=API:10m ключи и метаданные ~80 тысяч записей /var/cache/nginx/api тела ответов, по файлу на запись потолок - max_size proxy_cache_valid сколько ответ считается свежим inactive сколько запись живёт БЕЗ обращений valid короче inactive не случайно: протухшее пригодится, когда бэкенд ляжет
Кончится зона - записи вытесняются, даже если на диске место есть.

Правило

/etc/nginx/nginx.confhttp
proxy_cache_path /var/cache/nginx/api levels=1:2 keys_zone=API:10m
                 inactive=60m max_size=2g;
/etc/nginx/conf.d/shop.local.confhttp server location /api/
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 его уважает.

↳  выводбэкенд отдаёт Cache-Control: max-age=60, proxy_cache_valid не задан
X-Cache-Status: MISS
X-Cache-Status: HIT

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

Разбор: как понять, что происходит

/etc/nginx/conf.d/shop.local.confhttp server
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, которые обнуляются.

↳  выводпрогрели кеш, перезапустили nginx
до перезапуска:          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 в заголовке и в логе; кончается обычно зона ключей, а не место на диске.

Комментарии

Пока нет комментариев. Будь первым!

Оставить комментарий

Комментарий появится после проверки. Email не публикуется. Войти, чтобы не вводить имя каждый раз.