Job 2026 md

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 запросов в БД, пока ключ пересчитывается.

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

  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 §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/pull
  • methodology.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), CDN
  • materials/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 (эталоны, где эти фразы живут целиком)