Перейти к содержанию

D-03 · Стратегия кэширования#

Короткая форма — не полная минус разделы. У обратимого решения другая задача: не «выбрать правильно», а «не ошибиться катастрофически, быстро узнать об ошибке и суметь откатиться». Поэтому вес смещён с сравнения на дисквалификаторы, умолчание и измерение. Матрицы нет — вместо неё умолчание и таблица отклонений. Рыночного слоя нет: выбор продукта — отдельное решение, он в 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.


Проверка перед тем, как считать решение принятым — пять пунктов в шаблоне. Для короткой формы третий пункт читается иначе: «отложить» здесь означает «включить умолчание и померить», а не «не решать».

История версий#

Версия Дата Изменение
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 дисквалификаторов, умолчание с таблицей отклонений вместо матрицы, слитый раздел «что мерить и когда вернуться», запись в пять строк