Когда память кончилась, срабатывает политика eviction. Hot key греет один шард и один поток. Thundering herd — согласованный промах: все клиенты одновременно идут в базу.
Кэш и Redis redis собеседование
Три разные аварии часто называют одним словом «Redis лёг». Их лечат по-разному.
- Eviction — не влезло в память, ключ вытеснили. Для приложения это как истекший TTL: miss.
- Hot key — ключ влез, но принимает почти весь трафик. Греет один шард и один поток.
- Thundering herd (cache stampede) — ключ пропал у всех сразу, все пошли в Postgres.
Redis — кэш с ограниченной RAM, если вы сами не поставили «безлимит» и не удивились OOM. maxmemory задаёт потолок. Политика решает, что делать при записи сверх потолка: отказать или кого-то вытеснить. Кэш больше не «полный набор данных», а «что влезло».
Политики eviction
noeviction— запись получает ошибку, ключи сами не вытесняются. Честно, если Redis для вас не кэш, а store, и вы обязаны не молча терять данные.allkeys-lru/allkeys-lfu— вытесняют любой ключ, даже без TTL. Классический кэш.volatile-lru(и аналоги volatile-*) — только ключи с TTL. Ключ без expire живёт вечно и может забить память неприкасаемым множеством.
LRU в Redis приближённый, не идеальный двусвязный список на каждый ключ. Для разговора достаточно: «вытесняем давно не использованное, оценка приближённая». После eviction следующий GET — miss, и дальше уже stampede, если ключ популярный.
Hot key
Один ключ (витрина, токен инфлюенсера, глобальный конфиг) принимает 90% GET. В кластере ключ живёт на одном шарде и исполняется одним потоком. CPU этого шарда в потолке, остальные спят. Индекс в Postgres тут ни при чём.
Пример. Ключ product:1 — хит дня. Пятьсот подов делают GET именно его. Шард этого ключа на 100% CPU. Соседние шарды почти пустые. Добавить реплик приложения не поможет: все клиенты попадут на тот же слот.
Лечение: короткий локальный кэш в процессе (singleflight плюс TTL в сотни миллисекунд), копии значения под суффиксами и случайное чтение, read replica Redis для чистого чтения, не класть витрину в один mega-hash. Шардирование клиентов не поможет, если ключ один.
Thundering herd
Популярный ключ протух одновременно у всех — одинаковый TTL без jitter, либо eviction того же ключа. Пятьсот подов делают GET, ловят miss, все идут в Postgres, все делают SET. База ложится, Redis заливают одной и той же записью. Это stampede, не «медленный SQL».
-- stampede: 500 подов одновременно
GET product:1 -- miss
-- все идут в Postgres, потом SET product:1
-- смягчение: один загрузчик
SET product:1:lock 1 NX EX 30
-- кто выиграл NX — читает БД и SET product:1
-- остальные ждут lock или отдают протухшее значение
Согласованный miss: все поды одновременно идут в Postgres. SET NX как лок позволяет грузить базу одному, остальным — ждать или отдать stale.
Смягчение stampede: jitter на TTL (не всем ключам EX 60, а 60 ± 15), soft TTL / stale-while-revalidate (отдать старое и обновить в фоне), singleflight в процессе, лок вокруг загрузки. Eviction популярного ключа даёт тот же эффект, что истечение TTL: готовьте те же щиты.
Типичная ошибка
Смешать три диагноза в один. noeviction «потому что так в образце docker-compose» на кэше, который обязан вытеснять. Одинаковый TTL на горячем ключе без jitter. Лечить hot key добавлением реплик приложения — ключ всё равно на одном шарде Redis.
На собеседовании
Разведите вслух: eviction (не влезло в RAM), hot key (перекос нагрузки на один ключ/шард), stampede (согласованный miss в БД). Политика noeviction против allkeys-lru — осознанный выбор. На stampede назовите jitter, lock/singleflight и stale-while-revalidate.