Какие виды кэшей существуют и какие использовать для ускорения запросов?
Что проверяют
Понимаете ли вы, что кэш бывает на каждом слое, от браузера до базы, и что сложность не в том, чтобы положить данные, а в том, чтобы вовремя их сбросить. Следом спрашивают про инвалидацию и про локальный кэш при нескольких экземплярах сервиса.
Как отвечать
Пройдите по слоям:
- HTTP-кэш и CDN: заголовки
Cache-Control,ETag, ответ304 Not Modified. Запрос вообще не доходит до приложения; - локальный кэш в JVM: Caffeine (современный выбор, вытеснение W-TinyLFU), Ehcache. Доступ без сети, но у каждого экземпляра своя копия;
- распределённый кэш: Redis, Memcached, Hazelcast. Общий для всех узлов, но это сетевой вызов и сериализация;
- кэши Hibernate: первый уровень живёт в
EntityManagerи включён всегда, второй общий для фабрики сессий, по умолчанию выключен и подключается через JCache-провайдер; - кэш самой базы: буферный пул, в PostgreSQL это
shared_buffers.
В Spring слои склеивает абстракция: @EnableCaching, @Cacheable, @CachePut, @CacheEvict и CacheManager под Caffeine или Redis. Типичная схема: Caffeine для горячих справочников, Redis для данных, которые должны совпадать на всех узлах.
Назовите стратегии: cache-aside (приложение само читает кэш, при промахе идёт в базу), read-through, write-through, write-behind. И параметры: TTL, максимальный размер, политика вытеснения. Отдельно упомяните лавину промахов (cache stampede): ключ истёк, и сотня потоков одновременно идёт в базу. Caffeine в get(key, loader) вычисляет значение один раз на ключ, остальные потоки ждут результат.
Где спотыкаются
Вызывают @Cacheable-метод из того же класса. Вызов идёт мимо прокси, и кэш не срабатывает.
Кэшируют без ограничений. Провайдер Spring по умолчанию, ConcurrentMapCacheManager, не знает ни TTL, ни лимита размера, и кэш растёт до OutOfMemoryError.
Держат изменяемые данные в локальном кэше при нескольких экземплярах. Один узел сбросил запись, остальные ещё минуты отдают старое значение.