Инвалидация кэша
Инвалидация — удаление или обновление кэша, когда исходные данные изменились, чтобы кэш не показывал устаревшую информацию. Есть известная шутка про сложность этого: «в программировании есть только две сложные вещи: инвалидация кэша и придумывание имён».
@CacheEvict("users") // удалить из кэша при изменении
void updateUser(User user) {
userRepository.save(user);
}
Копнуть глубже
Три основных подхода:
- TTL (Time To Live) — самый простой: запись просто “протухает” через заданное время, независимо от того, изменились данные на самом деле или нет. Просто, но не идеально точно — может показать устаревшие данные в течение TTL, или зря пересчитать ещё не устаревшие.
- Явная инвалидация по событию — при каждом изменении данных явно удаляешь/обновляешь соответствующую запись кэша (
@CacheEvict/@CachePut). Точнее, но требует не забыть про инвалидацию в каждом месте, где данные меняются. - Версионирование/тегирование — кэш хранится с версией данных, и при изменении версии все связанные записи считаются недействительными разом — полезно, когда одно изменение должно “протухнуть” сразу много связанных кэш-записей.
Почему это считается сложным. Главная опасность — забыть проинвалидировать кэш в одном из мест, где данные меняются (особенно если изменение происходит не через обычный сервисный метод, а напрямую через SQL или из другого сервиса). Это приводит к незаметным багам: пользователь видит старые данные, и разработчик не сразу понимает, что причина — в кэше, а не в логике.
Практический совет: для данных, где устаревание на пару секунд/минут не критично — используй простой TTL, это надёжнее и проще поддерживать, чем пытаться идеально точно инвалидировать каждое изменение вручную.
• почему "забытая" инвалидация — частый источник незаметных багов (если дошёл до 2-го слоя).