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 §4; полные разборы — ../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 §2). Read-mostly данные селебрити (последние посты, профиль) — идеальный кандидат кэша; проговорить эту пару «горячий ключ → план» одной фразой.
7. Cache stampede (thundering herd)
Главный аварийный сюжет раздела — и на секции его спрашивают отдельно. Механика: тысяча горячих ключей истекает одновременно (или сбрасывается) → по каждому — промах → каждый читатель идёт в БД → БД, рассчитанная на 10 % трафика, получает 100 % и падает → сервис падает вместе с ней. Каскад из трёх характерных сценариев:
- Пачковый прогрев: ключи заполнили одной волной (после релиза/миграции) с одинаковым TTL — через TTL они же хором и истекут.
- Отказ/рестарт кластера: умирает нода (или кластер, или деплой обнулил кэш) — весь горячий набор холодный одновременно. Отказ ноды при consistent hashing — «только» 1/N ключей (статья 05 §7), но если нод мало, а ключи горячие — этого достаточно.
- Один сверхпопулярный ключ истёк: даже один ключ с 100k RPS — шторм из 100k запросов в БД, пока ключ пересчитывается.
Защиты — знать все три, называть в порядке дешевизны:
- Jitter на TTL (
TTL ± rand(20 %)) — ключи, пополненные пачкой, истекают размазанно. Ноль стоимости; закрывает сценарий №1. - Single-flight (распределённая блокировка пересчёта): при промахе ключ блокируется, один запрос идёт в БД и заполняет кэш, остальные ждут его результат (или отдают слегка стейл). Закрывает «сверхпопулярный ключ» и сильно смягчает рестарт. Реализация — лок в Redis/ZooKeeper с fencing (KB §13.5) или ожидание в приложении; в эталоне ленты — per-user lock на rebuild.
- Прогрев и превентивность: 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§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 — заготовки ответов «где ставите кэш?»:
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 §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 §7; канон — ../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 §6; сверка с эталонами — ../classic-designs.md §1–2, §4. Если три части вопроса звучат без запинки, а на «а если упадёт весь кэш?» рука тянется к headroom'у БД и load shedding, а не к «ну, поставим кэш заново» — раздел готов.
Связанные документы
00-overview.md— обзорная статья по всем 12 разделам брифа (TASK-45.40)02-estimations.md— правила 80/20 и латентности, на которых держится «зачем кэш»; примеры: KV-зеркало shortener (§5), горячие ленты 200 ГиБ (§6)03-scaling.md— кэш как ступень 5 лестницы масштабирования (§2); CDN: механика, offload, fallback (§4); stateless и запрет файлового кэша (§5)05-replication-sharding.md— consistent hashing и виртуальные ноды кэш-кластера (§7), hot partition (§8), строка «отказ ноды кэша» в таблице отказов (§10)../brief.md— бриф: раздел 06 с критерием «умею, если» (три вопроса: где / как инвалидирую / что при отказе)knowledge-base.md— §4 кэш (стратегии, инвалидация, stampede, hot key, метрики), §2 правило 80/20, §9 graceful degradation/load shedding, §13.8 CDN push/pullmethodology.md— §2 карточки этапов 3–6, §4 топ-10 ошибок, §6 протокол тренировок, §7 одностраничная карточка на секцию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— источник: кэш-слой, аспекты использования (TTL, SPOF, LRU), CDNmaterials/sdp-guide.md— источник: уровни кэша, объектный vs запросный, таблица стратегий обновления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(эталоны, где эти фразы живут целиком)