Карта / Кэширование / Инвалидация кэша

Инвалидация кэша

Инвалидация — удаление или обновление кэша, когда исходные данные изменились, чтобы кэш не показывал устаревшую информацию. Есть известная шутка про сложность этого: «в программировании есть только две сложные вещи: инвалидация кэша и придумывание имён».

@CacheEvict("users")        // удалить из кэша при изменении
void updateUser(User user) {
    userRepository.save(user);
}
Копнуть глубже

Три основных подхода:

  • TTL (Time To Live) — самый простой: запись просто “протухает” через заданное время, независимо от того, изменились данные на самом деле или нет. Просто, но не идеально точно — может показать устаревшие данные в течение TTL, или зря пересчитать ещё не устаревшие.
  • Явная инвалидация по событию — при каждом изменении данных явно удаляешь/обновляешь соответствующую запись кэша (@CacheEvict/@CachePut). Точнее, но требует не забыть про инвалидацию в каждом месте, где данные меняются.
  • Версионирование/тегирование — кэш хранится с версией данных, и при изменении версии все связанные записи считаются недействительными разом — полезно, когда одно изменение должно “протухнуть” сразу много связанных кэш-записей.

Почему это считается сложным. Главная опасность — забыть проинвалидировать кэш в одном из мест, где данные меняются (особенно если изменение происходит не через обычный сервисный метод, а напрямую через SQL или из другого сервиса). Это приводит к незаметным багам: пользователь видит старые данные, и разработчик не сразу понимает, что причина — в кэше, а не в логике.

Практический совет: для данных, где устаревание на пару секунд/минут не критично — используй простой TTL, это надёжнее и проще поддерживать, чем пытаться идеально точно инвалидировать каждое изменение вручную.

🎤 Закрыл тему, если можешь объяснить:
• зачем нужна инвалидация кэша и три подхода к ней;
• почему "забытая" инвалидация — частый источник незаметных багов (если дошёл до 2-го слоя).