коротко

Кэш — это быстрая копия данных, положенная ближе к потребителю, чтобы не пересчитывать их и не ходить в базу при каждом запросе. Нужен ради двух вещей: скорости (ответ из памяти приходит в разы быстрее) и снятия нагрузки (база и сервисы не надрываются). Кэшируют везде: в браузере, на CDN, внутри приложения, в отдельном хранилище вроде Redis. Если данные есть в кэше — это cache hit, если нет и пришлось идти в базу — cache miss. Главная боль — инвалидация: как понять, что копия устарела, и вовремя её обновить. Отсюда классическое «показывает старое». Для аналитика ключевой вопрос — не технический, а бизнесовый: насколько свежими должны быть эти данные.

Однажды я полдня доказывал команде, что баланс в личном кабинете «врёт». Пользователь пополнял счёт, видел старую сумму, психовал, пополнял ещё раз. Баг искали в платёжке, в базе, в логах списаний — всё было верно. Виноват оказался кэш: баланс кэшировался на минуту, и эта минута между пополнением и обновлением экрана рождала жалобы. Чинили не код, а решение: «баланс кэшируем ноль секунд, а вот курс валют — пять минут, и это нормально». Тогда я понял, что кэш — это не настройка бэкенда, а требование, которое должен поставить аналитик.

Что такое кэш и зачем он нужен

Кэш — это место, где лежит готовый ответ, чтобы не считать его заново. Представьте, что вас каждый день спрашивают, сколько будет 347 × 829. Можно умножать в столбик каждый раз, а можно один раз посчитать, записать ответ на стикер и приклеить к монитору. Стикер — это и есть кэш: копия результата, лежащая под рукой.

В системах считать заново дорого по двум причинам. Во-первых, скорость: достать данные из быстрой памяти — это микросекунды, а сходить в базу через сеть, выполнить запрос и вернуть результат — это миллисекунды, иногда сотни. Разница в тысячи раз. Во-вторых, нагрузка: если десять тысяч пользователей одновременно просят одну и ту же главную страницу, без кэша база получит десять тысяч одинаковых запросов и захлебнётся. С кэшем она ответит один раз, остальные получат копию.

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

Cache hit и cache miss на пальцах

Логика любого кэша одинакова: сначала загляни в кэш, и только если там пусто — иди за данными по-настоящему.

flowchart TD
  A["Запрос: дай данные"] --> B{"Есть в кэше?"}
  B -->|"да — cache hit"| C["Вернуть копию из кэша, быстро"]
  B -->|"нет — cache miss"| D["Сходить в БД, медленно"]
  D --> E["Положить ответ в кэш"]
  E --> F["Вернуть данные"]

Схема показывает путь запроса. Сначала система спрашивает кэш: «есть ли тут готовый ответ?». Если есть — это cache hit, копия возвращается сразу, быстро и без нагрузки на базу. Если нет — это cache miss: приходится идти в базу по-настоящему (медленно), а полученный ответ по дороге кладут в кэш, чтобы следующий такой же запрос стал уже hit. Чем больше доля hit'ов, тем быстрее и дешевле работает система. Метрика «процент попаданий в кэш» (hit rate) — то, за чем следят в проде; подробнее про метрики — в записи про наблюдаемость.

Где кэшируют

Кэш стоит не в одном месте, а слоями — от пользователя до базы. Каждый слой ловит часть запросов, чтобы они не доходили до следующего.

  • Браузер (HTTP-кэш) — браузер сам хранит у себя картинки, стили, скрипты, чтобы не качать их при каждом заходе. Управляется HTTP-заголовками (Cache-Control и компания). Самый ближний к пользователю кэш.
  • CDN — сеть серверов по всему миру, которые держат копии статики ближе к пользователю географически. Человек из Новосибирска получает картинку с сервера в Новосибирске, а не из Москвы. Снимает нагрузку и сокращает задержку.
  • Кэш приложения — сам сервис держит часто нужные данные у себя в памяти, чтобы не дёргать базу.
  • Отдельное хранилище (Redis) — выделенный сверхбыстрый сервер-кэш, общий для всех экземпляров приложения. Redis — это in-memory хранилище ключ-значение; в роли кэша он один из самых частых. Подробнее про такие хранилища — в записи про SQL и NoSQL.

Кэш — это про чтение, не про запись

Кэш ускоряет получение данных, которые читают часто, а меняют редко: каталог товаров, профиль, курс валют. Для данных, которые постоянно меняются и должны быть точны до копейки (остаток на складе в момент покупки, текущий баланс при списании), кэш либо не ставят, либо ставят с нулевым временем жизни. Понимать, что кэшируется, а что нет, — это и есть требование к свежести данных, и ставить его — работа аналитика.

Инвалидация: главная боль

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

Самый частый способ — TTL (time to live), время жизни копии. Кладём данные в кэш и говорим: «живи пять минут». Пять минут все получают копию (быстро, но, возможно, чуть устаревшую), через пять минут копия «протухает», следующий запрос идёт в базу за свежими данными и кладёт новую копию. TTL — это компромисс: чем он больше, тем быстрее и дешевле система, но тем дольше она может показывать устаревшее.

Вот почему пользователь «уже оплатил, а баланс старый»: данные закэшированы на минуту, оплата прошла, но копия ещё живёт свой TTL и отдаёт старое число. Система не сломана — она работает ровно как настроена. Проблема в том, что для баланса минута устаревания недопустима, а для курса валют — нормальна. Это решение, и принять его должен не разработчик наугад, а аналитик осознанно.

«В IT две сложные вещи»

Есть известная шутка: «В программировании всего две по-настоящему сложные задачи — инвалидация кэша и придумывание имён». Звучит как анекдот, но это правда из окопов. Положить в кэш умеет каждый. А вот гарантировать, что после изменения данных все копии везде — в браузере, на CDN, в Redis — обновятся вовремя и согласованно, — это честно трудно. Чем больше слоёв кэша, тем больше мест, где может застрять старое.

Кэш и асинхронность

Кэш — близкий родственник асинхронного мышления (см. синхронную и асинхронную интеграцию). Отдавая данные из кэша, система сознательно говорит: «вот ответ, возможно, чуть устаревший, зато сразу». Это та же логика, что и «приняли, посчитаем потом»: мы меняем абсолютную свежесть на скорость. Иногда обновление кэша вообще запускают фоновым процессом — данные пересчитываются в стороне, а пользователь всё это время получает последнюю готовую копию и никогда не ждёт. Главное — понимать, на какой именно компромисс мы пошли и где он допустим.

Что меняется для аналитика

Аналитик не настраивает Redis и не пишет заголовки Cache-Control. Но именно аналитик отвечает на вопрос, без которого настройка невозможна: насколько свежими должны быть эти данные? И это бизнес-вопрос, а не технический.

Спрашивайте по каждому экрану и каждой цифре: можно ли показать баланс минутной давности? А цену товара? А число лайков? А остаток на складе в момент оформления заказа? Ответы будут разными — и именно из них рождается настройка кэша.

ДанныеДопустимая устарелостьРешение по кэшу
Список категорий каталогаЧасыКэшируем долго, TTL в часах
Цена товараМинутыКороткий TTL, инвалидация при смене цены
Курс валютМинутыTTL 1–5 минут — приемлемо
Баланс счётаНоль (деньги)Не кэшируем или TTL = 0
Остаток на складе при покупкеНоль (продадим лишнее)Читаем напрямую из базы

Эта таблица — не теория, а готовый артефакт для спеки. Если вы принесёте разработчику такую табличку вместо общего «сделайте побыстрее», вопрос «а что можно кэшировать?» снимется сам собой, а пользователь не увидит чужой устаревший баланс.

Откуда это взялось

Идея кэша стара как сама архитектура компьютеров. Ещё в фон-неймановской модели память выстроена иерархией: крошечная сверхбыстрая память процессора — побольше и помедленнее — и огромная медленная. Каждый верхний уровень кэширует нижний, потому что быстрая память дорогая, а медленная — большая. Веб подхватил тот же принцип: HTTP-кэширование описали в стандартах протокола ещё в 1990-х, а компания Akamai около 1998 года построила первую большую CDN, раскидав копии контента по миру, чтобы он грузился ближе к пользователю. А крылатую фразу про «две сложные вещи в IT — инвалидацию кэша и именование» приписывают Филу Карлтону, инженеру Netscape.

Частые вопросы

Что такое инвалидация кэша простыми словами?

Это процесс, когда устаревшую копию данных в кэше помечают негодной или удаляют, чтобы система пошла за свежими данными. Без инвалидации кэш отдаёт старое: пользователь оплатил, а баланс прежний. Самый частый способ — TTL, время жизни копии: через заданный срок копия «протухает» сама и обновляется при следующем запросе. Инвалидацию считают одной из самых сложных задач в IT, потому что копий бывает много и обновить их все вовремя и согласованно непросто.

Чем cache hit отличается от cache miss?

Cache hit — это попадание: запрошенные данные нашлись в кэше, и их вернули сразу, быстро и без нагрузки на базу. Cache miss — промах: в кэше данных не оказалось, пришлось идти в базу (медленно), а полученный ответ положили в кэш на будущее. Чем выше доля попаданий (hit rate), тем быстрее и дешевле работает система. За этим показателем следят в проде как за метрикой здоровья кэша.

Какие данные нельзя кэшировать?

Те, где устарелость недопустима, — обычно связанные с деньгами и количеством в момент операции: баланс счёта при списании, остаток на складе в момент покупки, результат платежа. Если показать старую копию, можно дважды продать последний товар или ввести пользователя в заблуждение по деньгам. Такие данные либо не кэшируют вовсе, либо ставят им нулевое время жизни. Решение «можно ли это кэшировать и насколько» — это требование к свежести данных, и формулирует его аналитик.