[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"question-keshirovanie-chto-takoe-cache-stampede-i-kak-ego-predotvratit":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":20,"progress":21,"seo":22},185,"chto-takoe-cache-stampede-i-kak-ego-predotvratit",5,"keshirovanie","Кеширование","⚡","Что такое Cache Stampede и как его предотвратить?","Cache Stampede (Thundering Herd) — это ситуация, когда TTL горячего ключа истекает, и множество одновременных запросов обращаются к БД за одними и теми же данными, вызывая её перегрузку.\n\n> **Аналогия из жизни:** представьте кассу в магазине, которая закрылась на перерыв. Все покупатели из её очереди одновременно бросаются в соседнюю кассу, создавая давку, хотя достаточно было бы пустить одного, а остальным подождать.\n\n### Как происходит\n\n```\n1. Ключ \"popular-product\" в кэше (10 000 запросов\u002Fсек)\n2. TTL истёк → ключ удалён\n3. 100 запросов одновременно видят cache miss\n4. 100 одинаковых запросов к БД → перегрузка\n5. БД медленно отвечает → ещё больше запросов → каскадный сбой\n```\n\n### Решения\n\n### 1. Locking (единственный запрос обновляет кэш)\n\n\u003Cdetails>\u003Csummary>Пример кода\u003C\u002Fsummary>\n\n```java\npublic Product getProduct(Long id) {\n    Product cached = cache.get(\"product:\" + id);\n    if (cached != null) return cached;\n\n    \u002F\u002F Только один поток обновляет кэш\n    String lockKey = \"lock:product:\" + id;\n    if (redisTemplate.opsForValue().setIfAbsent(lockKey, \"1\", Duration.ofSeconds(10))) {\n        try {\n            Product product = productRepository.findById(id).orElseThrow();\n            cache.put(\"product:\" + id, product, Duration.ofMinutes(10));\n            return product;\n        } finally {\n            redisTemplate.delete(lockKey);\n        }\n    } else {\n        \u002F\u002F Другие потоки ждут или используют stale значение\n        Thread.sleep(50);\n        return getProduct(id); \u002F\u002F retry\n    }\n}\n```\n\n\u003C\u002Fdetails>\n\n### 2. Probabilistic Early Expiration (XFetch)\n\nКаждый запрос с некоторой вероятностью обновляет кэш до истечения TTL. Чем ближе к истечению — тем выше вероятность.\n\n### 3. Refresh-Ahead (Caffeine)\n\n```java\nLoadingCache\u003CLong, Product> cache = Caffeine.newBuilder()\n    .expireAfterWrite(Duration.ofMinutes(30))\n    .refreshAfterWrite(Duration.ofMinutes(25))  \u002F\u002F фоновое обновление за 5 мин до истечения\n    .build(id -> productRepository.findById(id).orElseThrow());\n```\n\n### 4. Stale-While-Revalidate\n\nВозвращать протухшее значение, пока фоновый поток обновляет кэш. Пользователь получает ответ мгновенно, а данные обновятся в фоне.\n\n### Сравнение решений\n\n| Решение | Сложность | Гарантии | Применимость |\n|---------|-----------|----------|-------------|\n| Locking | Средняя | Один запрос к БД | Redis (распределённый) |\n| Probabilistic Early Expiration | Средняя | Вероятностное | Любой кэш |\n| Refresh-Ahead | Низкая | Фоновое обновление | Caffeine (in-process) |\n| Stale-While-Revalidate | Средняя | Stale данные на время обновления | HTTP, CDN, custom |\n\n### Ключевые принципы\n\n- Cache Stampede опасен для горячих ключей с высоким RPS\n- Locking — самый надёжный, но добавляет сложность\n- Caffeine `refreshAfterWrite` — элегантное решение для in-process кэша\n- Для Redis — locking через `SETNX` + TTL\n\n### Частые ошибки\n\n- **Игнорировать проблему** — \"у нас не бывает\" до первой распродажи или launch event\n- **Lock без TTL** — если поток упал, lock навсегда, все ждут, deadlock\n- **Retry без backoff** — 100 потоков спамят retry каждые 50 мс, создавая ту же нагрузку\n\n### Как используется в 2026\n\n- Caffeine `refreshAfterWrite` — стандарт для L1\n- Redis locking через Redisson — для L2\n- CDN (Cloudflare, CloudFront) решают stampede на уровне edge\n\n> **На собеседовании:** интервьюер проверяет, сталкивались ли вы с проблемами производительности кэша на практике. Частая ошибка — не знать термин Cache Stampede и не иметь готового решения (locking или refresh-ahead).","","senior",[15,16,17,18,19],"thundering-herd","cache-stampede","locking","refresh-ahead","caching",[],null,{"title":23,"description":24,"ogTitle":25,"ogDescription":26,"keywords":27,"schemaAnswer":36,"featuredSnippetReady":37},"Что такое Cache Stampede и как его предотвратить — Gymterview","Cache Stampede (Thundering Herd): проблема массовых запросов к БД при истечении TTL горячего ключа. Решения: locking, Refresh-Ahead, XFetch, stale-while-revalidate.","Cache Stampede: что это и как предотвратить — Gymterview","Разбираем проблему Cache Stampede и 4 решения: locking, probabilistic early expiration, Refresh-Ahead, stale-while-revalidate.",[28,29,30,17,31,32,33,34,35],"Cache Stampede","Thundering Herd","кэш","Refresh-Ahead","refreshAfterWrite","Caffeine","Redis SETNX","Java","Cache Stampede (Thundering Herd) — ситуация, когда TTL горячего ключа истекает и множество одновременных запросов обращаются к БД, вызывая перегрузку. Решения: locking (только один поток обновляет кэш через SETNX), Probabilistic Early Expiration (обновление с вероятностью до истечения TTL), Refresh-Ahead (Caffeine refreshAfterWrite — фоновое обновление), Stale-While-Revalidate (возврат устаревшего значения во время обновления).",true]