---
title: Статья 06 — Кэширование
source: подготовлено 2026-09-17, TASK-45.46; каркас — brief.md §06; развёртка knowledge-base.md §4 (мосты в §2, §9, §13.8); источники — Alex Xu гл. 1 (TASK-45.38), SDP §7 (TASK-45.7), статья Яндекса L1/L2 (TASK-45.5), эталоны classic-designs.md §1–2, §4
---

# Статья 06: кэширование

Шестая статья серии — про копии данных, которые делают ради скорости, а не ради надёжности. Критерий «умею, если» из брифа: **объясняю, где ставлю кэш, как его инвалидирую и что будет при его отказе**. Обратите внимание: это ровно три вопроса, и на секции их задают именно в этом порядке — интервьюер по нему же и проверяет. В лестнице масштабирования из статьи 03 кэш — пятая ступень: после индексов, вертикали и read-реплик, до денормализации и шардов — самая поздняя «дешёвая» ступень, дальше каждая копия данных стоит уже сильно дороже.

Место раздела в каркасе: статья 03 нарисовала кэш как блок схемы и пообещала «TTL, вытеснение, инвалидацию — в раздел 06»; статья 05 уже пользовалась кэш-кластером (consistent hashing, горячие ключи, отказ ноды кэша в таблице отказов) — здесь эти темы разворачиваются целиком. Связка с соседями: размер кэша считается правилом 80/20 на этапе оценок (статья 02 §4), событийная инвалидация — это шина из раздела 07, поведение при отказе кэша — витрина graceful degradation из раздела 08, CDN как кэш статики уже разобран в статье 03 §4 (здесь — только то, чего там не было). Глубина — [`../knowledge-base.md`](../knowledge-base.md) §4; полные разборы — [`../classic-designs.md`](../classic-designs.md).

## 1. Зачем кэш: порядки чисел и цена копии

Две посылки, из которых существует весь раздел. Первая — из латентностей (статья 02 §3): **RAM на порядки быстрее диска** (десятки-сотни нс против единиц-десятков мс за HDD-seek) и не требует парсинга SQL-плана. БД на read-RPS уровня «миллионы пользователей» не рассчитана — и каждый мимо неё сэкономленный запрос экономит сразу три ресурса: её CPU, её диски и терпение пользователя. Вторая — из оценок: **нагрузка на чтение неравномерна, правило 80/20** — 20 % объектов дают ~80 % трафика (классический пример — ленты: полная суточная масса терабайты, горячие 20 % — пара сотен ГиБ, статья 02 §6). Кэшировать всё бессмысленно и дорого; кэшировать горячую долю — дёшево и снимает основную часть нагрузки.

Что кэш обещает (и за что платит) — три обещания-трейдоффа, их стоит держать вместе:

- **Латентность чтения**: микро- вместо миллисекунд. Плата: каждое чтение теперь может устареть.
- **Разгрузка БД**: hit ratio 90 % означает, что БД видит каждый десятый запрос. Плата: промах дороже, чем без кэша (две системы вместо одной: поход в кэш + поход в БД + запись в кэш).
- **Независимое масштабирование**: кэш-слой наращивается отдельно от БД. Плата: ещё распределённая система — со своими отказами, метриками и гонками (§5–8).

Железная граница из Сюя (гл. 1): **кэш — не постоянное хранилище**. Данные в энергозависимой памяти исчезают при рестарте; сервер кэша, на котором «живут» данные, а не их копии, — это уже не кэш, это БД с потерянными гарантиями (персистентность Redis — отдельная оговорка, не для этого). Отсюда формулировка-канва всего раздела: **«источник правды — БД; кэш — ускоритель над ней; всё, что я делаю с кэшем, должно это не ломать»**.

## 2. Уровни: где может лежать копия

Вопрос брифа «где ставите кэш?» предполагает ответ картой уровней, а не «в Redis». Полный маршрут копии — от клиента до диска БД:

| Уровень | Что кэширует | TTL / инвалидация | Чья зона ответственности |
|---------|--------------|-------------------|--------------------------|
| **Браузер / ОС клиента** | ответы HTTP (Cache-Control, ETag), DNS-резолвер — записи | секунды–сутки, заголовками | клиента; мы только советуем заголовками |
| **CDN** | статика (медиа, JS/CSS), изредка — динамические края (§9) | TTL от origin, версионированные URL | провайдера; наш источник правды — origin (§9) |
| **Reverse proxy / web-слой** (NGINX, Varnish) | отрендеренные страницы, статику на своём краю | короткий TTL, purge | наш ops-контур |
| **L1 — локальный кэш приложения** (in-process, heap/разделяемая память) | самое горячее, результаты «дорогих» вычислений | короткий TTL + LRU | приложения, на каждой ноде |
| **L2 — распределённый кэш-слой** (Redis/Memcached-кластер) | объекты горячего набора: сессии, профили, ленты | TTL + событийная инвалидация | общий для всех нод приложения |
| **Внутри БД** | buffer pool, кэш запросов/планов | движок сам | СУБД; считаем «бесплатным», но помним, что он есть |

Четыре уровня нижней половины — рабочая зона секции; клиент и DNS упоминаем одной фразой («заголовками отдаём политику кэширования клиенту и CDN»), не углубляясь.

**L1 против L2** — разница, которую стоит проговорить явно (эталон — короткие ссылки из статьи Яндекса, §1):

- **L1** живёт в процессе веб-сервера: доступ без сети (десятки нс), переживает даже отказ всего L2. Минусы жёсткие: памяти мало (мегабайты, не гигабайты), **инвалидация на все ноды сразу** (центра нет — событие должно долететь до каждой), каждая новая нода стартует холодной, и горячий набор в ней надо прогревать.
- **L2** один на все ноды приложения: гигабайты-терабайты, общий горячий набор, одна точка инвалидации. Плата: сетевой хоп (~0,5–1 мс) и отказ кластера как класс событий (§8).

Связка из эталона Яндекса (shortener): **L2 — read-only KV-зеркало** центральной СУБД (~100 ГБ, ~100 тыс. RPS на сервер, «не шардировать, а реплицировать»), **L1 — LRU-кэш на веб-сервере**: промах в L1 → чтение KV; свежая ссылка ещё не доехала до отстающего KV → поход в центральную СУБД → ответ кэшируется в L1. Одним ходом закрыты и read-your-writes для новых ссылок, и ограничение нагрузки на СУБД. Это готовая фраза для любой read-heavy системы: «L1 на ноде для самого горячего, L2 — общий, источник правды — СУБД».

Практическое «нет» из SDP §7: **файловый кэш на диске ноды избегаем** — он превращает «stateless-ноду, которую можно убить и склонировать» (статья 03 §5) в ноду со стейтом: клон бесполезен, пока не прогреется, автоскейлинг врёт, отладка больная. Кэш на вычислительной ноде — только in-memory с быстрым прогревом.

## 3. Что кэшируем: объекты, а не запросы

Выбор единицы кэширования — решение №1 после «где». Два класса:

- **Кэш запросов** (hash SQL-запроса → результат) — хрупок: изменение **одной ячейки** инвалидирует **все** запросы, в которых она участвовала; список зависимостей неотслеживаем. В боевых системах почти мёртв (query cache MySQL выпилен не на пустом месте) — знать как анти-паттерн.
- **Объектный кэш** (ключ → сериализованный объект: профиль, сессия, лента как список post_id, счётчик) — ключ выбираем сами, и он же определяет инвалидацию. Это дефолт.

Правило выбора ключа — то же, что для шард-ключа в статье 05 §6.2: **ключ = гранулярность горячего чтения**. Лента читается по пользователю — ключ `user_feed:<user_id>`; профиль — `profile:<user_id>`; пост — `post:<post_id>`. Если горячий запрос собирает данные из двух объектов — это два ключа и сборка, а не один «запросный» ключ.

Что **не** кэшируем (проговорить на секции — это половина ответа «что вы кэшируете?»):

- **Холодное и редко читаемое** — память стоит денег, 80/20 работает против: выживаем на горячей доле.
- **Пишемое чаще, чем читаемое** — кэш всё время промахивается и инвалидируется, получаем чистый минус (запись в кэш на каждый промах).
- **Требующее строгой согласованности** (баланс, лимиты) — либо не кэшируем вовсе, либо кэшируем с инвалидацией по факту записи и проговариваем окно рассогласования явно (мост в раздел 09: eventual — это осознанный выбор с размером окна, а не «почти угадал»).

## 4. Стратегии чтения и записи: пять паттернов

Главный выбор раздела. Таблица — канон KB §4 / SDP §7, расширенная под разговор:

| Стратегия | Как работает | Плюс | Минус |
|-----------|--------------|------|-------|
| **Cache-aside** (lazy loading) | приложение: читает кэш → промах → БД → записать в кэш; запись — в БД, кэш инвалидируется | просто; БД — источник правды; переживает отказ кэша насквозь | промах = три хода; стейл между инвалидацией и следующим заполнением (лечит TTL); холодная нода/ключ разгоняется на живом трафике |
| **Read-through** | кэш-слой сам ходит в БД при промахе | код приложения чище; логика заполнения в одном месте | требует кэша с этой логикой (не голый Redis-клиент); остальное — как cache-aside |
| **Write-through** | запись идёт сквозь кэш синхронно (кэш + БД одной операцией со стороны клиента) | прочитал-сразу-после-записи — свежее; консистентность кэша и БД сильнее | запись медленнее (две системы); пишем-но-не-читаем — память под ключ, который не нужен (лечится TTL); холодный старт нод — гибрид с cache-aside |
| **Write-behind (write-back)** | запись только в кэш, в БД — асинхронно батчами | write-производительность (микросекунды); батчи щадят БД | **потеря данных при падении кэша до сброса в БД** — проговаривать всегда; сложнее реализация; окно рассогласования БД↔кэша |
| **Refresh-ahead** | кэш сам пересчитывает популярные ключи **до** истечения TTL | нет «холодного ожидания» на горячих ключах; профилактика stampede (§7) | мимо прогноза — хуже, чем без него (лишние перерасчёты); нужен прогноз «горячести» |

Формулировка по умолчанию: **«cache-aside + TTL — базовый паттерн: БД остаётся источником правды, кэш опционален и переживает его отказ; write-through — там, где сразу после записи читаем; write-behind — только для данных, потеря которых за окно сброса допустима (лайки, просмотры, счётчики)»**. Оговорка про write-behind — маркер зрелости: интервьюер часто ждёт именно её (у Сюя и в SDP она выделена отдельным пунктом).

## 5. Инвалидация: «одна из двух сложных задач CS»

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

- **TTL — универсальная страховка, есть всегда.** Даже идеальная событийная инвалидация не отменяет TTL: гонки (ниже), баги, пропущенные события — всё лечится временем. Длина — трейдофф: короткий → чаще промахи и нагрузка на БД; длинный → шире окно стейла. На секции — назвать число: «профили — минуты, ленты — десятки минут, статика — сутки+».
- **Удаление по записи (delete), а не обновление (update).** При записи в БД ключ из кэша **удаляем**, следующий читатель сам заполнит свежим. Почему не update: параллельная запись в кэш и БД — два хода без транзакции, порядок непредсказуем, в кэше легко оседает «старое поверх нового». Удаление атомарно и всегда конвергентно.
- **Версионированные ключи** (`profile:v<123>` или смена префикса): «инвалидация» = публикация новой версии — все читатели атомарно переходят на новый ключ, старый истекает сам. Нет окна гонки вовсе; плата — памяти на две версии на время TTL. Так же версионируется статика в CDN: неизменяемый контент + новая версия в URL (`app.v17.js`) — из статьи 03 §4.
- **Событийная инвалидация через шину**: запись в БД → событие (CDC/binlog или outbox — мост в 05 §2.3 и 07) → консьюмер удаляет/прогревает ключи. Латентность инвалидации — секунды, без участия читателей. Плата: шина как зависимость и порядок событий (раздел 07).

**Классическая гонка cache-aside**, которую нужно уметь нарисовать на доске (2 минуты, 4 шага): (1) читатель A — промах, читает **старое** значение из БД; (2) писатель B пишет в БД новое; (3) B удаляет ключ; (4) A **кладёт старое в кэш** — уже после удаления. В кэше — протухшее значение на весь TTL. Лечения по нарастающей: короткий TTL (окно стейла ограничено); **delayed double delete** — B повторно удаляет ключ через паузу дольше «полёта» читателя A; версионированные ключи — гонки нет в принципе. Формулировка: «delete-on-write не закрывает гонку читателя с записью; на критичном — double delete или версионированный ключ, и всегда TTL как страховка».

## 6. Вытеснение: LRU, LFU и горячие ключи

Кэш конечен; когда память кончилась, вытеснение решает, кто остаётся:

- **LRU** (least recently used) — дефолт индустрии: временная локальность доступа («что недавно трогали — скоро тронут снова»). O(1) на операцию (двусвязный список + хеш). Его слабость — **одноразовый скан**: редкий тяжёлый обход (экспорт, обход бэкапом) вытесняет весь горячий набор (cache pollution).
- **LFU** (least frequently used) — по частоте: скан не трогает частоиспользуемые; берём для неравномерного доступа. Минус — «вчерашняя слава»: счётчики не забывают, старые горячие ключи застревают (лечится затуханием счётчиков).
- **FIFO** — самый простой и самый слабый (порядок прихода ≠ ценность); называем, чтобы отвергнуть.

Практика Redis одной строкой: `maxmemory` + `maxmemory-policy`: `allkeys-lru` (классический кэш), `volatile-lru` (вытеснять только с TTL — опасно: «вечные» ключи не вытесняются), `volatile-ttl` (вытеснять ближние к истечению). На секции достаточно: «Redis с allkeys-lru и maxmemory, весь кэш с TTL».

**Hot key (селебрити)** — отдельный сюжет, сквозной с шардированием (статья 05 §8): один ключ собирает 100k+ RPS — нода кэша, на которую он лёг, горит, остальные скучают. Три средства, по нарастающей цены: (1) **L1 на нодах приложения** — миллион читателей гасится локально, до L2 доживают единицы; (2) **реплики ключа** — `feed:<uid>:0..N` с разложением по хешу запроса, один логический ключ размазан по нодам; (3) выделение «селебрити» в особый путь (pull на чтении вместо push — эталон ленты, [`../classic-designs.md`](../classic-designs.md) §2). Read-mostly данные селебрити (последние посты, профиль) — идеальный кандидат кэша; проговорить эту пару «горячий ключ → план» одной фразой.

## 7. Cache stampede (thundering herd)

Главный аварийный сюжет раздела — и на секции его спрашивают отдельно. Механика: **тысяча горячих ключей истекает одновременно** (или сбрасывается) → по каждому — промах → каждый читатель идёт в БД → БД, рассчитанная на 10 % трафика, получает 100 % и падает → сервис падает вместе с ней. Каскад из трёх характерных сценариев:

- **Пачковый прогрев**: ключи заполнили одной волной (после релиза/миграции) с одинаковым TTL — через TTL они же хором и истекут.
- **Отказ/рестарт кластера**: умирает нода (или кластер, или деплой обнулил кэш) — весь горячий набор холодный одновременно. Отказ ноды при consistent hashing — «только» 1/N ключей (статья 05 §7), но если нод мало, а ключи горячие — этого достаточно.
- **Один сверхпопулярный ключ истёк**: даже один ключ с 100k RPS — шторм из 100k запросов в БД, пока ключ пересчитывается.

Защиты — знать все три, называть в порядке дешевизны:

1. **Jitter на TTL** (`TTL ± rand(20 %)`) — ключи, пополненные пачкой, истекают размазанно. Ноль стоимости; закрывает сценарий №1.
2. **Single-flight (распределённая блокировка пересчёта)**: при промахе ключ блокируется, **один** запрос идёт в БД и заполняет кэш, остальные ждут его результат (или отдают слегка стейл). Закрывает «сверхпопулярный ключ» и сильно смягчает рестарт. Реализация — лок в Redis/ZooKeeper с fencing (KB §13.5) или ожидание в приложении; в эталоне ленты — per-user lock на rebuild.
3. **Прогрев и превентивность**: refresh-ahead для горячего (пересчёт до истечения), управляемый прогрев после рестартов/релизов, для стейл-допустимых — отдача протухшего значения при промахе под нагрузкой (stale-while-revalidate).

Формулировка: «против stampede у меня три слоя: jitter, чтобы ключи не истекали хором; single-flight, чтобы пересчёт делал один запрос, а не все; прогрев и refresh-ahead после рестартов. Это же — моя защита нисходящего трафика при отказе ноды кэша».

## 8. Что будет при отказе кэша

Обязательный пункт брифа и AC этой статьи: ответ «что будет при его отказе» зависит от того, **что именно отказало** — прогон по четырём случаям (в таблице отказов статьи 05 §10 этому соответствует строка «Нода кэша»; здесь она разворачивается):

| Что отказало | Что происходит | Что делаю | Метрика, где видно |
|--------------|----------------|-----------|--------------------|
| **Одна нода кэш-кластера** | Волна промахов по «своим» ~1/N ключей; трафик по ним уходит в БД | consistent hashing с виртуальными нодами (переезд 1/N, не всего набора) + single-flight на пересчёт; ключи остальных нод не задеты | hit rate ↓, RPS/латентность БД ↑ — всплеск, затем конвергенция |
| **Весь кэш-слой / кэш-регион** | Весь read-трафик падает на БД, рассчитанную на остаток | БД обязана пережить полный read-поток (capacity planning с headroom) + rate limiting / load shedding / graceful degradation (KB §9): отдаём стейл, деградируем фичи, а не падаем | hit rate ~0, p99 БД ↑ — проверяем, что БД не каскадит |
| **Кэш с write-behind** | **Потеря записей**, не сброшенных в БД, — единственный случай, когда отказ кэша = потеря данных | поэтому write-behind только для допустимых к потере данных (счётчики, лайки); окно сброса — секунды; для важного — write-through | расхождение кэша и БД; потери фиксируются сверкой |
| **Холодный старт** (после деплоя, при скейле нод, у новой L1-ноды) | Кэш жив, но пуст: первые читатели медленные, БД видит пиковые промахи | прогрев горячего набора перед вводом трафика, canary-налив, refresh-ahead; L1 прогревается сам (быстрый TTL) | hit rate по ноде с нуля; время разогрева — дежурная метрика релиза |

Две позиции, которые нужно произнести на секции дословно-образно:

- **«Кэш — ускоритель, а не источник правды»** (при условии, что мы не write-behind): его отказ деградирует латентность, но не данные. Сервис без кэша **медленный, но живой** — если, конечно, БД спроектирована с запасом. Отсюда правило capacity: **БД держит 100 % read-потока или есть явный план shedding'а** — «кэш умер → умирает всё» — провал секции.
- **Профилактика SPOF**: один сервер кэша — единая точка отказа (Сюй прямо это называет); кластер с виртуальными нодами, headroom по памяти и CPU, кэш-ноды в разных ДЦ (или хотя бы стойках); мониторинг — hit rate, evictions, латентность (KB §4: «метрики кэша обязательны»).

Отсюда мост в раздел 08: отказ кэша — эталонный пример graceful degradation («чем жертвуем: свежестью и скоростью, не доступностью»), а cold-start после релиза — сюжет canary-выката из раздела 12.

## 9. CDN — кэш с географией

CDN уже разобран в статье 03 §4 (механика pull-заполнения, TTL от origin, offload 90 %+, fallback на origin при отказе края). Здесь — три дополнения с этого ракурса, которых там не было:

- **CDN — это кэш**, поэтому вся логика раздела переносится: TTL = cache-control, «инвалидация» = версионированные URL (неизменяемый контент + новая версия в имени), hit ratio края — та же метрика. Разница принципиальна одна: **инвалидацию напрокат нельзя** — у провайдера нет «удалить объект из всех краёв» с гарантией (purge-запросы есть, но асинхронны и не мгновенны), поэтому контент в CDN делают неизменяемым по имени. Это та же идея версионированных ключей §5, но доведённая до железного правила.
- **Origin shield** (из эталона медиа, [`../classic-designs.md`](../classic-designs.md) §4): над origin ставится промежуточный слой кэша провайдера, и когда новый сегмент видео нужен на N краёв, в origin летит **один** промах, а не N. Без shield'а редкий контент пробивает origin сквозь весь CDN — это CDN-версия stampede.
- **Push/pull/hybrid** (KB §13.8): горячее проталкиваем на край заранее (push), редкое тянем по промаху (pull), обычно гибрид. Плюс экономика: трафик CDN платный, холодный контент из CDN убираем (статья 03 §4.3).

Динамическое кэширование (HTML по пути/кукам/заголовкам) существует — упомянуть как «знаю, но не по умолчанию»: сложность вариативности ключа ломает кэшируемость на корню. Для нашей секции CDN = статика + медиа; динамику обслуживает свой стек L1/L2.

## 10. Метрики: hit ratio и его арифметика

Кэш без метрик — нерассуждающий кэш. Дежурная тройка:

- **Hit ratio** — главный показатель; цель для hot path — **80–90 %+** (KB §4: «90 %+»). Арифметика для секции: нагрузка на БД = `RPS × (1 − hit)`; hit 95 % → БД видит 1/20 потока; hit 99 % → 1/100. Обратная задача — планирование: «хочу снять с БД 10× → нужен hit ≥ 90 %» — произносится как связка с оценками (статья 02 §5: «35 тыс. QPS ÷ 100 тыс. RPS на KV-ноду — кэш не горлышко»).
- **Evictions** (вытеснения по памяти) — растут → горячий набор не влезает → добавлять память или сузить кэшируемое; evictions при недоиспользованной памяти = misconfigured policy.
- **Латентность кэша** (p95/p99) и латентность промаха — кэш «есть», но медленный — это отдельный диагноз; хвосты кэша тянутся в хвосты сервиса (та же «хвостовая амплификация», что в эталоне ленты).

Плюс размер занятой памяти к потолку maxmemory и время разогрева после рестарта. Формулировка: «hit ratio, evictions, p99 кэша — у меня дежурные графики; hit rate падение — первый сигнал и отказа ноды, и смены трафика, и бага инвалидации».

## 11. Прикладное: как это звучит в эталонах

Три системы из [`../classic-designs.md`](../classic-designs.md) — заготовки ответов «где ставите кэш?»:

**URL shortener (эталон Яндекса, §1).** 500k RPS чтения, read/write 100:1 → **L2: read-only KV-зеркало** `link_id → {url, account_id}` (~100 ГБ — «не шардировать, а реплицировать», инстанс рядом с каждым воркером — локальность), **L1: LRU на веб-сервере** для read-your-writes свежих ссылок и предохранителя центральной СУБД. Зеркало обновляется потоком изменений из СУБД; TTL — страховка. Фраза: «кэш здесь и есть data plane — реплицированное KV-зеркало, потому что чтений на два порядка больше записей».

**Лента новостей (§2).** Кэш лент — Redis-кластер, ключ `user_feed:<user_id>` (consistent hashing), значение — **только post_id, глубиной ~800** (не контент! — контент это отдельный кэш постов/CDN для медиа); 80/20 → ~130 ГБ горячих. Stampede — per-user lock на rebuild; свой пост — синхронно в свою ленту + L1 (read-your-writes); селебрити — pull на чтении + агрессивный кэш последних постов (read-mostly — идеальный кандидат). Фраза: «лента — это предвычисленный кэш: fan-out on write наполняет его событиями, чтение — один гет по user_id».

**Медиа-хостинг (§4).** Кэш = CDN: offload 90 %+, сегменты иммутабельны (max-age большой, инвалидация не нужна — новая версия = новое имя), origin shield против пробоя промахами, прогрев первых сегментов популярных видео (старт ≤ 2 с), signed URLs против хотлинка. Фраза: «для блобов кэш — это CDN, и он же разгрузка egress; у себя кэшировать медиа бессмысленно».

Сборка одного ответа на вопрос брифа «где ставлю кэш»: **«по горячему пути, уровнями: статика — CDN (неизменяемая, версионированная), динамика — L2 Redis-кластер по гранулярности чтения, самое горячее/селебрити — L1 на нодах; источником правды остаётся БД; вот отсюда и инвалидация, и поведение при отказе»** — 20 секунд, три уровня, мост в §5 и §8.

## 12. Ошибки этого раздела

Подмножество топ-10 ошибок ([`../methodology.md`](../methodology.md) §4) и анти-паттернов источников:

| Ошибка | Как выглядит | Противоядие |
|--------|--------------|-------------|
| Кэш как постоянное хранилище | «Пишем в Redis, БД нет» | Кэш — копия; источник правды — БД (§1); write-behind — с явной оговоркой о потерях (§4, §8) |
| Нет TTL | «Данные живут, пока память не кончится» | TTL всегда, даже с событийной инвалидацией — страховка от гонок и багов (§5) |
| Update вместо delete при записи | В кэше оседает «старое поверх нового» | Delete-on-write; на критичном — double delete / версионированный ключ (§5) |
| Один сервер кэша | SPOF: отказ кэша = отказ сервиса | Кластер + виртуальные ноды + headroom; БД держит полный поток или shedding (§8) |
| Нет ответа «что при отказе кэша» | Замешкнулся на прямом вопросе брифа | Таблица §8: нода / слой / write-behind / холодный старт — по строкам |
| TTL без jitter + пачковый прогрев | Массовое истечение → stampede → падение БД | Jitter + single-flight + прогрев (§7) |
| Файловый кэш на ноде | Нода «stateless на словах» | Только in-memory L1 с быстрым прогревом (§2) |
| Кэш запросов | Инвалидация «все запросы с этой ячейкой» | Объектный кэш, ключ = гранулярность чтения (§3) |
| Кэшируем всё подряд | Холодные данные жгут память, hit не растёт | 80/20: кэшируем горячую долю (§1, §3) |
| «Кэш снизит нагрузку» без числа | Offload не посчитан | `нагрузка на БД = RPS × (1 − hit)`; цель hit — от требования к разгрузке (§10) |

## 13. Что назвать на секции (чек-лист раздела)

Обязательные фразы и действия. Полный чек-лист — [`../methodology.md`](../methodology.md) §7; канон — [`../knowledge-base.md`](../knowledge-base.md) §4.

**На этапе 3–4 (схема и детали):**
- При первом блоке кэша на схеме: уровни одной фразой — «CDN для статики, L2 Redis для объектов, L1 на нодах для самого горячего; внутри БД — buffer pool, его не трогаем».
- Стратегия + ключ: «cache-aside с TTL, ключ — гранулярность горячего чтения: лента по user_id, пост по post_id».
- Hot key: «селебрити — L1 + реплики ключа, а лучше особый путь (pull)».
- Stampede: «jitter TTL, single-flight на пересчёт, прогрев после рестартов» — тремя словами каждый, разворачиваются по запросу.
- Вытеснение: «LRU дефолт, LFU при неравномерном доступе; Redis allkeys-lru с maxmemory».

**На этапе 6 (эксплуатация):**
- Прогон §8 таблицей: отказ ноды → отказ слоя → write-behind → холодный старт, каждой строке — метрика (hit rate, RPS БД, p99).
- «БД спроектирована пережить отказ кэша: headroom или rate limiting — кэш умер, сервис деградирует, а не умирает».
- «Мониторю hit ratio, evictions, p99 кэша» — три слова, закрывающие вопрос наблюдаемости.
- Связь с соседями: отказ ноды кэша — consistent hashing из 05; событийная инвалидация — шина из 07; деградация — 08.

**Сквозные формулы раздела:**
- «Источником правды остаётся БД; кэш — ускоритель» — проверяется именно на write-behind (потери) и отказе слоя (данные целы).
- «Ключ = гранулярность горячего чтения» — общая формула с шардированием (05 §6.2).
- «Нагрузка на БД = RPS × (1 − hit)» — кэш обосновывается числом, как и всё в статье 02.

## 14. Самопроверка «умею, если»

Критерий раздела из брифа: **объясняю, где ставлю кэш, как его инвалидирую и что будет при его отказе**. Проверка — прогон с закрытыми материалами: три части вопроса подряд, без пауз, на материале эталонов (shortener / лента / медиа). Часть 1 «где»: карта уровней §2 + точка на схеме с числом (80/20 из оценок). Часть 2 «как инвалидирую»: TTL + delete-on-write одной фразой, потом гонка читателя-писателя и её лечение (double delete / версионированные ключи / событийная) — по требованию. Часть 3 «что при отказе»: таблица §8 по строкам с метриками; сюда же stampede и его три защиты. Контрольное время: весь ответ — 4–6 минут этапа 4–6. Протокол тренировок и рубрика 0–3 — [`../methodology.md`](../methodology.md) §6; сверка с эталонами — [`../classic-designs.md`](../classic-designs.md) §1–2, §4. Если три части вопроса звучат без запинки, а на «а если упадёт весь кэш?» рука тянется к headroom'у БД и load shedding, а не к «ну, поставим кэш заново» — раздел готов.

## Связанные документы

- [`00-overview.md`](00-overview.md) — обзорная статья по всем 12 разделам брифа (TASK-45.40)
- [`02-estimations.md`](02-estimations.md) — правила 80/20 и латентности, на которых держится «зачем кэш»; примеры: KV-зеркало shortener (§5), горячие ленты 200 ГиБ (§6)
- [`03-scaling.md`](03-scaling.md) — кэш как ступень 5 лестницы масштабирования (§2); CDN: механика, offload, fallback (§4); stateless и запрет файлового кэша (§5)
- [`05-replication-sharding.md`](05-replication-sharding.md) — consistent hashing и виртуальные ноды кэш-кластера (§7), hot partition (§8), строка «отказ ноды кэша» в таблице отказов (§10)
- [`../brief.md`](../brief.md) — бриф: раздел 06 с критерием «умею, если» (три вопроса: где / как инвалидирую / что при отказе)
- [`knowledge-base.md`](../knowledge-base.md) — §4 кэш (стратегии, инвалидация, stampede, hot key, метрики), §2 правило 80/20, §9 graceful degradation/load shedding, §13.8 CDN push/pull
- [`methodology.md`](../methodology.md) — §2 карточки этапов 3–6, §4 топ-10 ошибок, §6 протокол тренировок, §7 одностраничная карточка на секцию
- [`classic-designs.md`](../classic-designs.md) — §1 URL shortener (L1/L2, KV-зеркало), §2 лента (кэш лент по user_id, селебрити, stampede-lock), §4 медиа (origin shield, прогрев сегментов)
- [`materials/alex-xu-vol1/ch-01-scaling-from-zero.md`](../materials/alex-xu-vol1/ch-01-scaling-from-zero.md) — источник: кэш-слой, аспекты использования (TTL, SPOF, LRU), CDN
- [`materials/sdp-guide.md`](../materials/sdp-guide.md) — источник: уровни кэша, объектный vs запросный, таблица стратегий обновления
- [`materials/yandex-564132.md`](../materials/yandex-564132.md) — источник: L1 LRU / L2 KV-зеркало, KV рядом с воркером, read-your-writes для новых ссылок
- Соседние статьи: `07-queues-streams.md` (событийная инвалидация и прогрев через шину) · `08-fault-tolerance.md` (graceful degradation при отказе кэша) · `09-consistency.md` (окно eventual как осознанный трейдофф) · `11-reference-designs.md` (эталоны, где эти фразы живут целиком)
