Redis: кеш, который не превращается в базу
Cache-aside, инвалидация, вытеснение — и лавина запросов, когда популярный ключ истёк.
Базовый паттерн: cache-aside
Приложение сначала спрашивает кеш, при промахе идёт в базу и кладёт результат в кеш с TTL:
value = redis.get(key)
if value is None: # промах
value = db.query(...)
redis.set(key, value, ex=300) # TTL обязателен
return value
TTL — не опция, а страховка: любая ошибка инвалидации со временем исправится сама, потому что ключ истечёт. Кеш без TTL — это вторая база данных, только без гарантий.
Инвалидация
Знаменитая «одна из двух сложных проблем» на практике сводится к выбору:
- только TTL — просто и предсказуемо: данные могут отставать максимум на TTL. Подходит всему, что не критично к свежести;
- удаление при записи — обновили запись в базе →
DELключа. Следующее чтение перезаполнит кеш. Свежее, но каждое место записи обязано помнить про кеш; - обновление кеша при записи — почти всегда хуже удаления: легко записать в кеш то, что база в итоге откатила.
Практичный дефолт: TTL везде + явный DEL там, где устаревание заметно пользователю.
Память и вытеснение
Redis живёт в RAM, поэтому лимит и политика вытеснения должны быть заданы явно:
maxmemory 2gb
maxmemory-policy allkeys-lru
Для чистого кеша allkeys-lru — правильный выбор: при нехватке памяти вытесняются давно не читанные ключи. Дефолтная политика noeviction для кеша опасна: память кончилась — Redis начинает отклонять записи, и кеш тихо перестаёт обновляться.
Лавина (cache stampede)
Популярный ключ истёк — и тысяча запросов одновременно промахнулась и пошла в базу. База, годами жившая за кешом, такого залпа не переживает.
Защиты, от простой к сложной:
- джиттер TTL —
300 + random(0, 60)секунд, чтобы ключи не истекали синхронно; - лок на пересчёт — только один запрос перезаполняет ключ, остальные ждут или отдают старое значение;
- фоновое обновление горячих ключей до истечения.
Кеш — не хранилище
Всё в Redis должно быть восстановимо из базы. Если потеря инстанса Redis теряет данные (корзины, сессии единственного экземпляра, счётчики) — это уже не кеш, и у такого Redis должны быть persistence (AOF), реплика и бэкапы — со всеми обязанностями настоящей базы.