[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"question-keshirovanie-chto-takoe-mnogourovnevoe-keshirovanie-l1-l2":3},{"id":4,"slug":5,"topicId":6,"topicSlug":7,"topicName":8,"topicEmoji":9,"question":10,"answer":11,"codeLang":12,"codeSrc":12,"important":12,"commonMistakes":12,"modernUsage":12,"difficulty":13,"tags":14,"related":21,"progress":22,"seo":23},184,"chto-takoe-mnogourovnevoe-keshirovanie-l1-l2",5,"keshirovanie","Кеширование","⚡","Что такое многоуровневое кэширование (L1 + L2)?","Многоуровневое (multi-level) кэширование — это архитектурный подход с использованием нескольких уровней кэша с разной скоростью и ёмкостью, где типичная схема включает L1 (in-process, Caffeine) и L2 (distributed, Redis).\n\n> **Аналогия из жизни:** L1 + L2 кэш — это как карманный блокнот (L1) и общий справочник в офисе (L2). В блокноте мало места, но он всегда под рукой. Справочник общий для всех сотрудников и содержит больше информации, но до него нужно дойти.\n\n### Схема работы\n\n```\nЗапрос → L1 (Caffeine, \u003C1мс) → L2 (Redis, ~1мс) → БД (~5мс)\n```\n\n### Реализация через CompositeCacheManager\n\n\u003Cdetails>\u003Csummary>Пример кода\u003C\u002Fsummary>\n\n```java\n@Configuration\n@EnableCaching\npublic class MultiLevelCacheConfig {\n\n    @Bean\n    public CacheManager cacheManager(RedisConnectionFactory redisFactory) {\n        \u002F\u002F L1 — Caffeine (in-process)\n        CaffeineCacheManager caffeineManager = new CaffeineCacheManager();\n        caffeineManager.setCaffeine(Caffeine.newBuilder()\n            .maximumSize(1_000)\n            .expireAfterWrite(Duration.ofMinutes(5))); \u002F\u002F короткий TTL для L1\n\n        \u002F\u002F L2 — Redis (distributed)\n        RedisCacheManager redisManager = RedisCacheManager.builder(redisFactory)\n            .cacheDefaults(RedisCacheConfiguration.defaultCacheConfig()\n                .entryTtl(Duration.ofMinutes(30))) \u002F\u002F длинный TTL для L2\n            .build();\n\n        \u002F\u002F Композитный CacheManager\n        return new CompositeCacheManager(caffeineManager, redisManager);\n    }\n}\n```\n\n\u003C\u002Fdetails>\n\n### Кастомная реализация с явным управлением\n\n\u003Cdetails>\u003Csummary>Пример кода\u003C\u002Fsummary>\n\n```java\n@Service\npublic class UserService {\n    private final LoadingCache\u003CLong, User> l1Cache;  \u002F\u002F Caffeine\n    private final RedisTemplate\u003CString, User> redis;  \u002F\u002F Redis\n    private final UserRepository repository;\n\n    public User findById(Long id) {\n        return l1Cache.get(id, key -> {\n            \u002F\u002F L1 miss → проверяем L2\n            User fromRedis = redis.opsForValue().get(\"user:\" + key);\n            if (fromRedis != null) return fromRedis;\n\n            \u002F\u002F L2 miss → БД\n            User fromDb = repository.findById(key).orElseThrow();\n            redis.opsForValue().set(\"user:\" + key, fromDb, Duration.ofMinutes(30));\n            return fromDb;\n        });\n    }\n\n    public void evict(Long id) {\n        l1Cache.invalidate(id);               \u002F\u002F инвалидировать L1\n        redis.delete(\"user:\" + id);           \u002F\u002F инвалидировать L2\n        \u002F\u002F + Redis Pub\u002FSub для инвалидации L1 на других экземплярах\n    }\n}\n```\n\n\u003C\u002Fdetails>\n\n### Сравнение уровней\n\n| Характеристика | L1 (Caffeine) | L2 (Redis) |\n|---------------|---------------|------------|\n| Latency | \u003C1 мс | ~1 мс |\n| Расположение | В heap JVM процесса | Отдельный сервер |\n| Видимость | Только текущий экземпляр | Все экземпляры |\n| Ёмкость | Ограничена heap | Ограничена RAM сервера Redis |\n| TTL | Короткий (1-5 мин) | Длинный (10-60 мин) |\n\n### Ключевые принципы\n\n- L1 (Caffeine) — менее 1 мс, ограничен размером heap; L2 (Redis) — около 1 мс, разделяемый между экземплярами\n- L1 TTL \u003C L2 TTL — L1 обновляется чаще, чтобы не расходиться с L2\n- Инвалидация L1 при обновлении данных — нужен Redis Pub\u002FSub или Kafka для уведомления всех экземпляров\n\n### Частые ошибки\n\n- **L1 без инвалидации между экземплярами** — экземпляр A обновил данные, экземпляр B отдаёт старые из L1\n- **Одинаковый TTL для L1 и L2** — L1 должен быть короче, чтобы периодически подтягивать из L2\n- **Слишком большой L1** — Caffeine в heap; при 100K объектов x 1KB = 100MB heap\n\n### Как используется в 2026\n\n- Caffeine (L1) + Redis (L2) — стандартная комбинация для high-traffic приложений\n- Библиотеки: `caffeine-redis-cache`, `spring-boot-starter-cache` с кастомным `CacheManager`\n- В Spring Boot нет multi-level из коробки — нужна кастомная конфигурация или сторонняя библиотека\n\n> **На собеседовании:** интервьюер проверяет понимание зачем нужны два уровня и как решается проблема инвалидации L1 в кластере. Частая ошибка — не упомянуть Pub\u002FSub для синхронизации L1-кэшей между экземплярами.","","senior",[15,16,17,18,19,20],"l1-cache","multi-level","caffeine","l2-cache","caching","redis",[],null,{"title":24,"description":25,"ogTitle":26,"ogDescription":27,"keywords":28,"schemaAnswer":36,"featuredSnippetReady":37},"Что такое многоуровневое кэширование (L1 + L2) — Gymterview","Многоуровневое кэширование L1 (Caffeine) + L2 (Redis) в Spring Boot. Реализация CompositeCacheManager, инвалидация между экземплярами, типичные ошибки.","Многоуровневое кэширование L1 + L2: Caffeine и Redis — Gymterview","Как реализовать L1 (Caffeine, \u003C1мс) + L2 (Redis, ~1мс) кэширование в Spring Boot. Конфигурация и инвалидация.",[29,30,31,32,33,34,35],"многоуровневое кэширование","L1 L2 кэш","Caffeine Redis","multi-level cache","CompositeCacheManager","Spring Cache","Java","Многоуровневое кэширование — использование нескольких уровней кэша: L1 (in-process, Caffeine, \u003C1мс) + L2 (distributed, Redis, ~1мс). Запрос проходит L1 → L2 → БД. L1 TTL должен быть короче L2 TTL. Инвалидация L1 между экземплярами реализуется через Redis Pub\u002FSub. В Spring реализуется через CompositeCacheManager или кастомный сервис.",true]