D-03 · Стратегия кэширования#
- Версия: 3.1 · 2026-09-17 · по мастер-шаблону v6.0
- Известно также как: кэш, caching strategy, cache policy, «поставить редис»
- Этап: W5 · План масштабирования · инвалидация проверяется на W6, поведение при отказе кэша — на W7
- Стадия: гринфилд · работающая
- Профиль: дёшево изменить · высокое влияние на экономику → короткая форма
- Не решает: поведение системы при полном отказе кэша — это D-06; выбор продукта под кэш — D-01; где проходят границы, между которыми встаёт кэш, — D-02
- Не путать с: D-01 — там выбирают, где лежит истина, а не чем снять чтения
- Карточки: CACHE-01 уровни кэша · CACHE-02 стратегии чтения и записи · CACHE-03 инвалидация · CACHE-04 патологии · REL-04 деградация
Короткая форма — не полная минус разделы. У обратимого решения другая задача: не «выбрать правильно», а «не ошибиться катастрофически, быстро узнать об ошибке и суметь откатиться». Поэтому вес смещён с сравнения на дисквалификаторы, умолчание и измерение. Матрицы нет — вместо неё умолчание и таблица отклонений. Рыночного слоя нет: выбор продукта — отдельное решение, он в D-01.
Область решения — одно подтверждённое узкое место, а не «кэширование в системе». Второе место — второе применение этого документа.
1. Что выяснить#
Пять вопросов вместо семи — и почти все закрываются замером, а не обсуждением.
| Вопрос | Где смотреть | Зачем |
|---|---|---|
| Что именно узкое место — подтверждено замером? | Метрики источника, профилирование | Без этого кэш ускоряет то, что и так быстро, и прячет настоящую проблему |
| Каково соотношение чтений к записям у этих данных и сколько уникальных ключей? | Логи запросов за сутки | Определяет достижимый hit ratio; при ключах «по одному на запрос» кэш бесполезен |
| Какая устарелость допустима — в секундах, отдельно для каждого типа чтения? | Требования, продукт | Задаёт TTL и способ инвалидации |
| Что произойдёт, если кэш целиком исчезнет в час пик? | Расчёт по замерам источника | Дисквалификатор, а не любопытство |
| Сколько стоит промах и сколько стоит попадание — в миллисекундах и в деньгах? | Тарифы, замеры | Экономика решения; ради неё короткая форма и заводится |
2. Оси и дисквалификаторы#
Оси сравнения — четыре, и они нужны не для выбора между шестью вариантами, а для проверки одного: достижимый hit ratio · допустимая устарелость · стоимость промаха против стоимости хранения · цена отката.
Дисквалификаторы — в обратимом решении именно они несут нагрузку: ошибиться можно дёшево, а вот эти пять ошибок дёшево обойтись не могут.
- Система не переживает полного отказа кэша в час пик. Кэш нельзя вводить как несущий элемент, пока не спроектирована деградация — это D-06, отдельное решение: иначе обратимое решение становится необратимым в момент аварии.
- Устарелость недопустима вовсе — остаток в момент списания, право доступа после отзыва, удалённый контент. Умолчание ниже выглядит подходящим (в нём та же инвалидация и тот же TTL), но отличается обязательствами, а не стратегией:
| Умолчание | Здесь | |
|---|---|---|
| Что означает TTL | Страховка от рассинхронизации; величина настраивается по нагрузке | Верхняя граница ущерба от потери инвалидации; меняется как требование, а не как настройка |
| Потеря события инвалидации | Допустима: ключ протухнет сам | Недопустима без обнаружения: нужна сверка выборкой, иначе нарушение идёт молча |
| Чем подтверждается работоспособность | Hit ratio | Hit ratio не меняется при сломанной инвалидации. Нужен отдельный показатель — доля ответов с устаревшим значением |
Стратегия та же, режим эксплуатации другой. Не различить эти два случая — самый дешёвый способ нарушить требование, не увидев ни одной ошибки в логах.
- Write-behind для данных, потеря которых недопустима. Падение кэша означает потерю (CACHE-02); для денег и заказов это дисквалификатор, а не trade-off.
- Уникальных ключей почти столько же, сколько запросов. Hit ratio будет околонулевым: кэш добавит задержку, стоимость и ещё один режим отказа, не дав ничего.
- Узкое место не подтверждено ни замером, ни расчётом. Кэш поверх догадки — самый дорогой способ не решить проблему, потому что после него проблему уже труднее увидеть. Расчёт достаточен: «восемь тысяч чтений в секунду против одного узла» выводится из объявленных чисел и проверяется на бумаге. Догадка «кажется, упрёмся в базу» — нет.
3. Умолчание и отклонения#
Вместо матрицы. Варианты здесь не равноправны: один заведомо разумен, остальные — оптимизации под названное условие.
Умолчание: cache-aside + удаление ключа при записи + короткий TTL как страховка. Переживает падение кэша, не теряет данные, держит только реально запрашиваемое (CACHE-02).
Отклоняются только по подтверждённому условию — не по ожидаемому:
| Условие | Отклонение | Чем платим |
|---|---|---|
| Данные меняются чаще, чем читается ключ | Короткий TTL вместо инвалидации | Устарелость в пределах TTL — и её надо согласовать с продуктом |
| Свежесть кэша важнее задержки записи | Write-through + read-through | Запись медленнее на операцию; кэш держит и невостребованное |
| Большой поток записи, потери допустимы — счётчики, телеметрия | Write-behind | Падение кэша означает потерю данных |
| Пишут часто, читают редко | Write-around | Первое чтение всегда промах |
| Популярность ключей предсказуема, пересчёт дорогой | Refresh-ahead | Тратится на данные, которые могли не понадобиться |
| Задержка до кэша сравнима с пересчётом | Локальный кэш в процессе | Рассогласование между экземплярами и холодный старт на каждом выкате |
| Данные статичны и раздаются многим | CDN или edge | Инвалидация становится медленной операцией |
Первая строка — главная развилка читающего кэша, и она обгоняет умолчание. Прежде чем выбирать стратегию записи, посчитайте время жизни ключа: частота записей в него делит единицу. Если ключ живёт меньше, чем проходит между двумя его чтениями, «удаление при записи» — не стратегия, а отказ кэша работать. На прогоне E-01 300 записей в секунду в одну схему зала дали ключу время жизни 3 мс: умолчание оказалось неработоспособным, а правильным ходом — не инвалидировать вовсе.
Отклонения складываются, а цены — нет. Таблица устроена по строке на отклонение и выглядит так, будто берут одно. Брать можно несколько, но тогда цену считают заново, а не суммируют по строкам: одно отклонение способно погасить цену другого. На прогоне E-02 сложились локальный кэш и обновление по событию, и цена второго — «тратится на данные, которые могли не понадобиться» — исчезла именно потому, что кэш локальный: в карте лежали только те ключи, ради которых события и приходили. Записывать в ADR надо пересчитанную цену, иначе в последствиях окажется работа, которой не будет.
У локального кэша цена двойная, и вторая половина обычно забывается. Рассогласование между экземплярами видно сразу. Второе — отказ локального кэша совпадает с перезапуском процесса, а перезапуски при автоматическом выкате не аварийные, а плановые и частые: свежий экземпляр с пустым кэшем отдаёт неполный ответ, и это не инцидент, а штатное состояние несколько раз в день. Отсюда обязательная работа: прогрев до приёма трафика, а не после.
Почему таблица отклонений, а не матрица. Матрица нужна, когда варианты сопоставимы и выбор делается один раз. Здесь заполнять шесть колонок, чтобы в большинстве случаев прийти к умолчанию, — работа впустую. Ошибка стоит дней: дешевле начать с умолчания и отклониться, когда условие подтвердится, чем обосновывать отклонение заранее.
Силы против кэша — короткий встречный список#
По образцу D-02: у решения есть встречная половина, и в короткой форме она короткая.
| Сила против | Чем оплачивается | Когда отменяет кэш |
|---|---|---|
| Ещё один режим отказа | Спроектированная деградация — не идея, а принятое D-06 | Деградации нет и не будет: кэш станет несущим элементом, и обратимое решение перестанет быть обратимым |
| Инвалидация как постоянная работа | Механизм плюс сверка выборкой | Инвалидацию нечем проверить, а устарелость недопустима |
| Стоимость памяти на единицу снятой нагрузки | Считается против реплики чтения и против индекса | Реплика или индекс дешевле — тогда это не кэш, а другое решение |
| Кэш скрывает причину | Замер до кэша, сохранённый рядом с решением | Узкое место не подтверждено: кэш не решит проблему и спрячет её |
Первая и последняя уже работают дисквалификаторами. Две средние — доводы, которые взвешиваются: они не запрещают кэш, но обязаны быть оплачены названной ценой в ADR.
4. Эвристики#
Сначала померить, потом кэшировать. W5 ставит кэш третьим шагом — после замера и вертикального масштабирования. Не из консерватизма: кэш скрывает проблему, а не устраняет, и после него найти настоящую причину заметно труднее.
Низкий hit ratio означает не «надо больше памяти», а «кэшируется не то». Добавление памяти в этой ситуации — самая частая и самая дорогая ошибка: она превращает дешёвую диагностику в постоянную статью расходов.
TTL начинать с самого короткого, который снимает нагрузку. Увеличить его легко, а объяснить, почему пользователь видит вчерашние данные, — нет.
Ничья между стратегиями — берём умолчание. Обратимость здесь не фигура речи: смена стратегии чтения обычно укладывается в одну задачу.
«Нет данных» здесь не повод откладывать — но и не повод не считать. Сначала расчёт: он бесплатен и часто снимает вопрос. Если расчёт не даёт ответа, включить умолчание на части трафика и померить.
Почему обратимость меняет смысл нехватки данных. В необратимом решении (D-01) пустая ячейка означает «не решать, пока не измерим». Здесь наоборот: включить умолчание на части трафика и померить — дешевле, чем анализ, который эту же цифру только оценит. Обратимость меняет смысл нехватки данных на противоположный.
5. Что мерить и когда вернуться#
В полной форме это два разных раздела. В обратимом решении они сливаются: показатель и есть условие возврата, и разносить их по разным местам — способ не заметить ни того, ни другого.
| Показатель | Вернуться к решению, когда |
|---|---|
| Hit ratio | Устойчиво ниже расчётного — кэшируется не то, что запрашивают |
| Доля устаревших ответов (сверка выборкой) | Превысила допустимую по ответу на третий вопрос |
| Нагрузка на источник при холодном кэше | Показывает, что систему отказ кэша не переживёт |
| Стоимость кэша на единицу снятой нагрузки | Перестала окупаться: дешевле реплика чтения или индекс |
| p99 обращения к кэшу | Сравнима с пересчётом — кэш стал лишним сетевым участком |
Срок пересмотра не назначается. Решение обратимо: его пересматривает метрика, а не календарь.
6. Запись#
Короткая форма не порождает полноценный ADR — матрицы нет, снимка рынка нет, копировать нечего. Записывается короткая версия: в ADR проекта, если решение заметно в деньгах, иначе в комментарий к конфигурации.
Кэш: cache-aside + delete on write, TTL 60 с, ключ <...>
Основание: чтений к записям 40:1, уникальных ключей ~200 тыс,
допустимая устарелость 2 мин [замер 2026-09-16]
Отклонения: нет
Вернуться: hit ratio ниже расчётного или доля устаревших выше допустимой
Метод: D-03 v1.0
Пять строк. Если понадобилось заметно больше — проверьте, не полная ли это форма: скорее всего появился рыночный слой, и выбор продукта нужно вынести отдельным решением.
Применений может быть несколько, и они бывают связаны. Одно узкое место — одно применение, но два кэша на одном пути чтения делят бюджет памяти и складываются в задержку, а иногда вытекают из одного параметра. Пяти строк на каждое в этом случае мало: сумма не видна нигде. Общей формы для связанных решений в наборе пока нет — она в 96-backlog.md; до тех пор такие применения записываются в один ADR с общим контекстом и общими последствиями, как сделано в E-02.
Проверка перед тем, как считать решение принятым — пять пунктов в шаблоне. Для короткой формы третий пункт читается иначе: «отложить» здесь означает «включить умолчание и померить», а не «не решать».
- Источники: AWS — Caching patterns · Scaling Memcache at Facebook (NSDI 2013) ·
00-workflow.md, шаг W5 (порядок применения средств масштабирования)
История версий#
| Версия | Дата | Изменение |
|---|---|---|
| 3.1 | 2026-09-17 | MINOR. Дисквалификатор «система не переживает отказа кэша» и строка «не решает» теперь указывают на D-06 — документ, которого этот дисквалификатор требовал дважды и которого до сих пор не было. Метод не изменился |
| 3.0 | 2026-09-17 | MAJOR, по итогам прогона E-02. Дисквалификатор «устарелость недопустима» разведён с умолчанием: стратегия та же, режим эксплуатации другой — TTL как верхняя граница ущерба, обязательная сверка выборкой, отдельный показатель вместо hit ratio. Сказано, что отклонения складываются, а цены пересчитываются, а не суммируются. Названа вторая цена локального кэша — холодный старт на каждом выкате и обязательный прогрев. Добавлен встречный список сил против кэша. Старым записям: если устарелость была недопустима, проверить, есть ли сверка выборкой — без неё нарушение идёт молча; если отклонений было больше одного, пересчитать цену |
| 2.0 | 2026-09-16 | MAJOR, по итогам прогона E-01. Добавлена главная развилка читающего кэша — инвалидация против короткого TTL — и она обгоняет умолчание: при частых записях в один ключ «удаление при записи» неработоспособно. Снято противоречие между дисквалификатором «не подтверждено замером» и эвристикой «нет данных не повод откладывать»: расчёт — достаточное основание, догадка — нет. Старым записям: посчитать время жизни ключа; если оно меньше интервала между чтениями, решение принято по неработоспособному умолчанию |
| 1.0 | 2026-09-16 | Первая версия и первая короткая форма: 5 вопросов, 4 оси, 5 дисквалификаторов, умолчание с таблицей отклонений вместо матрицы, слитый раздел «что мерить и когда вернуться», запись в пять строк |