Gymterview
middle

Какие стратегии кэширования существуют?

Кэширование – хранение часто запрашиваемых данных в быстром хранилище для снижения нагрузки на основной источник данных и уменьшения времени отклика. Как шпаргалка, в которую заглядываешь вместо того, чтобы каждый раз открывать учебник.

1. Cache-Aside (Lazy Loading)

Приложение само управляет кэшем. При промахе читает из БД и кладёт в кэш.

Пример
    Запрос → Есть в кэше?
              ├── Да  → Вернуть из кэша
              └── Нет → Прочитать из БД → Записать в кэш → Вернуть
Пример кода
@Service
public class AccountService {

    private final AccountRepository repository;
    private final RedisTemplate<String, Account> redisTemplate;

    public Account findById(Long id) {
        String key = "account:" + id;
        Account cached = redisTemplate.opsForValue().get(key);
        if (cached != null) return cached;

        Account account = repository.findById(id)
            .orElseThrow(() -> new NotFoundException(id));
        redisTemplate.opsForValue().set(key, account, Duration.ofMinutes(30));
        return account;
    }
}

Плюс: кэшируются только реально запрашиваемые данные. Минус: первый запрос всегда медленный (cache miss); данные могут устаревать до истечения TTL.

2. Write-Through

При записи данные сохраняются одновременно в кэш и в БД.

Пример
    Запись → Обновить кэш → Обновить БД → Ответ

Плюс: кэш всегда актуален. Минус: увеличенная задержка записи; кэшируются данные, которые могут никогда не читаться.

3. Write-Behind (Write-Back)

Данные записываются в кэш немедленно, а в БД – асинхронно с задержкой.

Пример
    Запись → Обновить кэш → Ответ
                  │
                  └── Асинхронно (через N мс) → Обновить БД

Плюс: очень быстрая запись. Минус: риск потери данных при сбое кэша до синхронизации с БД. Этот подход не подходит для финансовых операций, где потеря данных недопустима.

4. Read-Through

Кэш сам обращается к БД при промахе (в отличие от Cache-Aside, где это делает приложение). Приложение всегда обращается к кэшу и не знает об источнике данных.

5. Refresh-Ahead

Кэш обновляет данные до истечения TTL, если они часто запрашиваются. Предотвращает cache miss для горячих данных.

Сравнение стратегий

Стратегия Кто читает из БД Кто пишет в БД Консистентность Задержка записи
Cache-Aside Приложение Приложение Возможны stale data Нет (пишет напрямую)
Write-Through Кэш Кэш (синхронно) Высокая Увеличена
Write-Behind Кэш Кэш (асинхронно) Возможна потеря Минимальная
Read-Through Кэш Приложение Возможны stale data Нет
Refresh-Ahead Кэш (проактивно) Приложение Высокая для hot data Нет

Инструменты кэширования в Java

Инструмент Тип Применение
Redis Распределённый Кэш между микросервисами, сессии, лимиты
EhCache Локальный (in-process) Кэш внутри одного приложения
Caffeine Локальный (in-process) Быстрый кэш в памяти JVM, рекомендуется для новых проектов
Hazelcast Распределённый Distributed cache, in-memory data grid

Использование Spring Cache

Spring предоставляет абстракцию кэширования через аннотации, скрывая конкретную реализацию:

Пример кода
@Service
public class ClientService {

    @Cacheable(value = "clients", key = "#id")
    public ClientDto findById(Long id) {
        return clientRepository.findById(id)
            .map(ClientMapper::toDto)
            .orElseThrow();
    }

    @CacheEvict(value = "clients", key = "#id")
    public void updateClient(Long id, UpdateClientRequest request) {
        // обновление клиента в БД
    }

    @CacheEvict(value = "clients", allEntries = true)
    public void clearCache() { }
}

Типичные проблемы кэширования

  • Cache Stampede (thundering herd) – множество одновременных запросов при промахе кэша, все идут в БД. Решение: блокировка (mutex) при подгрузке или вероятностное раннее обновление.
  • Stale data – устаревшие данные. Решение: TTL, активная инвалидация при обновлении.
  • Cache Invalidation – одна из двух самых сложных проблем в computer science (Phil Karlton). Особенно сложна в распределённых системах с несколькими инстансами приложения.

На собеседовании: Интервьюер хочет услышать не просто перечисление стратегий, а понимание trade-off каждой. Частая ошибка – не упоминать проблемы инвалидации и Cache Stampede.