Sobes.tech
Назад к вопросам
Senior
6

Что такое cache stampede и cache penetration? Как с ними бороться?

Компании, где спрашивали
прайм пейментс

Ответ от нейросети

sobes.tech AI

Cache stampede — ситуация, когда множество запросов одновременно пытаются получить данные из кеша, но при этом кеш пуст или устарел, и все эти запросы начинают одновременно обращаться к медленному источнику данных (например, базе), создавая нагрузку.

Cache penetration — ситуация, когда запросы постоянно обращаются к кешу с ключами, которых там нет (например, из-за отсутствия данных в базе), и каждый такой запрос приводит к обращению к базе, что может привести к перегрузке.

Как бороться:

Cache stampede:

  • Использовать механизм блокировки (mutex) при обновлении кеша: первый запрос обновляет кеш, остальные ждут.
  • Применять стратегию "early recomputation" — обновлять кеш до истечения срока жизни.
  • Использовать "probabilistic early expiration" — обновлять кеш с вероятностью до истечения TTL.

Cache penetration:

  • Кешировать отрицательные результаты (например, пустые или null значения) с коротким TTL, чтобы не обращаться к базе повторно.
  • Валидировать входные данные, чтобы не допускать запросов с некорректными ключами.
  • Использовать фильтры (например, Bloom filter) для предварительной проверки наличия ключа в базе.

Пример на Java с использованием блокировки для cache stampede:

private final ConcurrentHashMap<String, Object> cache = new ConcurrentHashMap<>();
private final ConcurrentHashMap<String, ReentrantLock> locks = new ConcurrentHashMap<>();

public Object getData(String key) {
    Object value = cache.get(key);
    if (value != null) return value;

    ReentrantLock lock = locks.computeIfAbsent(key, k -> new ReentrantLock());
    lock.lock();
    try {
        // повторная проверка после захвата блокировки
        value = cache.get(key);
        if (value == null) {
            value = loadFromDb(key); // медленная операция
            cache.put(key, value != null ? value : NULL_PLACEHOLDER);
        }
        return value == NULL_PLACEHOLDER ? null : value;
    } finally {
        lock.unlock();
    }
}

Такой подход помогает избежать одновременного обращения к базе при отсутствии кеша.