Короткий ответ
Cache stampede — ситуация, когда популярный кешированный ключ истекает, и множество одновременных запросов «проваливаются» в основной источник данных сразу, создавая всплеск нагрузки на БД; предотвращают блокировкой пересчёта, ранним обновлением или джиттером TTL.
Как это работает подробнее
Без защиты каждый из N одновременных запросов, увидевших cache miss, независимо идёт в БД за одними и теми же данными и независимо записывает результат обратно.
Пример кода
example.py
def get_or_lock(key, compute_fn, ttl=300):
value = cache.get(key)
if value is not None:
return value
lock_acquired = cache.set(f"lock:{key}", "1", ex=10, nx=True)
if lock_acquired:
value = compute_fn()
cache.set(key, value, ex=ttl + random.randint(0, 30)) # jitter
cache.delete(f"lock:{key}")
return value
time.sleep(0.05)
return cache.get(key) or compute_fn()Что использовать на практике
- Практика. Решения — распределённая блокировка (только один процесс пересчитывает), stale-while-revalidate (отдавать старое значение во время фонового обновления), случайный разброс (jitter) TTL.
Подводные камни
- Риск. Ставить одинаковый фиксированный TTL для всех записей, закешированных в один момент (например, при прогреве) — массовое синхронное истечение почти гарантирует stampede под нагрузкой.