Кэш (cache)
Кэш (cache)
Быстрое временное хранилище копий данных, к которым часто обращаются, чтобы не ходить каждый раз в медленный и дорогой первоисточник.
История
Идея родилась в 1965 году у британского инженера Мориса Уилкса (того самого, что построил один из первых компьютеров EDSAC в Кембридже в 1949-м). В статье «Slave memories and dynamic storage allocation» он предложил маленькую быструю память-«раба» между процессором и основной памятью: раб хранит то, что процессор трогал недавно, и отдаёт это в разы быстрее, чем основной сундук. Слово «cache» (от французского cacher — прятать) как термин ввёл редактор журнала IBM Systems Journal Лайл Джонсон, когда статью Уилкса перепечатывали — «slave memory» звучало политически неудобно.
Первой коммерческой машиной с кэшем стал IBM System/360 Model 85 (1968) — шкаф размером с два холодильника. Идея сработала: L1-кэш добавил 30% скорости при 10% росте цены. С этого момента кэш стал стандартом.
Дальше вехи:
- 1985. Intel 386 — первый массовый процессор с внешним кэшем. Компьютер на столе получает то, что раньше было у мейнфреймов.
- 1989. Intel 486 — L1 переехал внутрь самого процессора (8 КБ). Скорость подскочила.
- 1995. Netscape 1.0 — браузер начал кэшировать картинки на диск. Тогда трафик стоил дорого, и каждая недокачанная гифка экономила деньги.
- 1997. Появляется Memcached — распределённый кэш в оперативке для веб-приложений. Его написали для LiveJournal, чтобы не убить базу. Пользуются до сих пор.
- 1998. Стартует Akamai — первая крупная CDN (Content Delivery Network), которая раскладывает копии сайтов по всему миру. По сути огромный географический кэш.
- 2009. Salvatore Sanfilippo выпускает Redis — кэш нового поколения, умеющий не только «положи-возьми», но и списки, множества, атомарные счётчики. Сегодня Redis — де-факто стандарт кэша в вебе.
- 2010–наши дни. Кэш есть на каждом уровне: CPU (L1/L2/L3), диск (SSD как кэш HDD), браузер, DNS-резолвер, CDN, БД, приложение. Мы живём внутри многослойного пирога из кэшей и обычно даже не замечаем.
Что это такое
Кэш — это посредник между быстрым потребителем и медленным источником. Потребитель просит данные; посредник смотрит, есть ли у него свежая копия; если есть — отдаёт мгновенно (это называется cache hit, попадание). Если нет — идёт в источник, забирает, кладёт себе, отдаёт (cache miss, промах). Следующий такой же запрос уже будет hit.
Ключевая метрика — hit rate (доля попаданий). CDN Cloudflare публично хвалится 85–95% hit rate на статику; кэш веб-приложений — 70–90%; кэш процессора L1 — 95%+. Каждый лишний процент hit rate — минус запрос в БД или на диск.
Кэш почти всегда ограничен по объёму (иначе это уже полная копия источника, а не кэш). Поэтому нужна политика вытеснения — какие данные выкидывать, когда место кончилось. Самая частая — LRU (Least Recently Used, «самое давно не используемое»): выкидывается то, к чему дольше всего не обращались. Есть ещё LFU (Least Frequently Used — по частоте), FIFO, random.
Чем кэш отличается от буфера. Буфер сглаживает разницу в скорости между продюсером и консюмером (буфер клавиатуры, буфер записи на диск) — данные через него проходят один раз. Кэш держит те же данные для повторных чтений.
Кэш vs репликация БД. Реплика — это полная синхронная копия базы; кэш — частичная, ленивая, обычно допускает лёгкое отставание. Реплика гарантирует консистентность, кэш ею жертвует ради скорости.
Кэш vs индекс. Индекс — это структура данных внутри БД для быстрого поиска (например, B-tree); он не хранит копию данных, только указатель. Кэш хранит именно копию.
Аналогии из жизни
Холодильник. Магазин далеко и работает по расписанию — это медленный источник. Холодильник дома быстрый: открыл, взял молоко. Пока молоко есть и не испортилось — living the dream. Где ломается: молоко просрочилось, а ты этого не знаешь, потому что смотрел только «есть/нет». Отравился. В кэше это называется stale data — старая, но формально доступная копия. Именно поэтому все системы кэширования упираются в вопрос: как понять, что копия устарела.
Записная книжка с телефонами. Каждый раз спрашивать Google «телефон стоматологии» — медленно и дорого (трафик, время). Записать один раз в книжку — быстро. Где ломается: стоматология переехала и сменила номер. Ты набираешь старый — попадаешь в квартиру. Это классическая проблема cache invalidation (инвалидации): как узнать, что записанное больше не актуально, если тебя никто не предупредил.
Оперативная память мозга — «где ключи?». Ты кладёшь ключи в одно и то же место каждый день, и мозг подставляет ответ автоматически, не задумываясь. Быстро. Где ломается: вчера ты положил ключи в другое место, но кэш в голове по-прежнему выдаёт старый ответ. Ты идёшь в привычное место, ключей нет, тратишь 20 минут на поиск. Тот же паттерн, что с молоком: копия есть, но она врёт.
Как это работает
Схема запроса через кэш выглядит так:
Потребитель ─(1)─► Кэш
│
├─ ЕСТЬ и свежее? ─── да ──► (2a) вернуть копию ─► Потребитель
│
└─ нет ──► Источник ─(3)─► Кэш кладёт себе ─(4)─► Потребитель
Стрелка (2a) — это hit; путь (3-4) — miss. Miss всегда дороже, чем прямое обращение к источнику (лишний накладной поход в кэш), поэтому кэш имеет смысл только когда hit rate высокий.
Как определяется «свежесть». Три классических подхода:
- TTL (time-to-live). Каждой записи ставится срок жизни. «Этот ответ живёт 60 секунд, потом выбрасываем.» Простой, устойчивый, но грубый: если данные не менялись — зря выкинули; если поменялись — до 60 секунд отдаём враньё.
- Пассивная инвалидация (по запросу). Кэш держит копию, пока её не выселит политика вытеснения. Свежесть проверяется условным запросом к источнику: «а вот эта версия ещё актуальна?» Именно так работает HTTP-кэш через заголовки
ETagиIf-None-Match— сервер отвечает304 Not Modified(не менялось), и потребитель использует свою копию. - Активная инвалидация (push). Источник сам говорит кэшу: «выбрось запись X, я её изменил». Быстро и точно, но требует прямого канала между источником и кэшем. Так работают CDN Cloudflare с вебхуками «purge» и Redis с
pub/sub-каналами.
Cache-aside vs write-through vs write-back — три разных ответа на вопрос «а что делать, когда клиент не читает, а пишет».
- Cache-aside (side-cache, самый частый): приложение пишет только в источник, кэш узнаёт об изменении по TTL или явной инвалидации. Просто, но копия отстаёт.
- Write-through: приложение пишет одновременно в источник и в кэш. Кэш всегда свежий, зато запись медленнее.
- Write-back (write-behind): приложение пишет только в кэш, а кэш асинхронно доносит изменения до источника. Максимум скорости, минимум надёжности — если кэш упадёт до досбра, данные пропадут. Так работают CPU-кэши и, например, дисковые контроллеры с батарейкой.
Многоуровневые кэши. В процессоре: L1 (~1 нс, десятки КБ), L2 (~10 нс, сотни КБ), L3 (~30 нс, десятки МБ), потом RAM (~100 нс), потом SSD (~100 000 нс), потом сеть. Каждый следующий уровень медленнее и больше. То же самое в вебе: браузерный кэш → CDN → реверс-прокси (nginx) → кэш приложения (Redis) → БД. Отсутствие в одном уровне спускает запрос на следующий.
Знаменитая цитата
«В computer science есть только две по-настоящему сложные вещи: инвалидация кэша и придумывание имён.» — Фил Карлтон, инженер Netscape, ~1996. Первое звучит смешно, но эта строчка объясняет большую часть багов в веб-приложениях за последние 25 лет. Всё, что «то работает, то нет», «у меня в браузере старая версия», «в отчёте старые цифры» — это чаще всего кэш, который никто не подчистил.
Где встречается в обычной жизни
- Загружаешь страницу второй раз — она открывается мгновенно. Браузер положил картинки, шрифты и стили себе на диск (папка Cache в профиле Chrome). При Ctrl+F5 браузер намеренно игнорирует кэш и качает всё заново.
- Ищешь «пиццерия рядом», Google подсказывает адрес быстрее, чем успел допечатать. Автодополнение — это кэш популярных запросов на серверах Google + локальная история в браузере.
- Открыл видео на YouTube в Москве — оно летит без буферизации. Это не сервер в Калифорнии отвечает: копия видео уже лежит на CDN-сервере в Москве (или даже в дата-центре твоего провайдера). Первый зритель тянет из США, все следующие — из соседней стойки.
- Приложение банка мгновенно показывает баланс, а операция «пополнить» заметно медленнее. Баланс закэширован (можно чуть отстать), пополнение — сквозной запрос в реальную БД (нельзя отстать ни на копейку).
- Вводишь «avito.ru», компьютер сразу знает IP-адрес. DNS-кэш — сначала в браузере, потом в системе, потом у провайдера. Полный поход к корневым DNS-серверам занял бы секунду; из кэша — микросекунды.
Где встречается в IT и бизнесе
- CDN на статике. Cloudflare, Akamai, CloudFront, Fastly — глобальный кэш картинок, JS, CSS. Разгружают исходный сервер в десятки раз, сокращают время загрузки для удалённых пользователей.
- Redis/Memcached перед БД. Классика: приложение сначала спрашивает Redis; если промах — идёт в PostgreSQL и кладёт ответ в Redis. База спит спокойно даже при десятках тысяч RPS. Все крупные соцсети и маркетплейсы устроены так.
- Application cache внутри процесса. Загруженная в память таблица курсов валют, справочник городов, конфиг — вместо чтения с диска каждый раз. Например, в приложениях на Java часто используют Caffeine или Guava Cache.
- HTTP-кэш (браузер + прокси). Заголовки
Cache-Control,ETag,Expires— контракт между сайтом и любым посредником о том, что и как кэшировать. Правильно настроенный HTTP-кэш даёт бесплатное ускорение без единой строчки на сервере. - CPU-кэш и оптимизация под него. В high-load-коде (игры, БД, ML) программисты специально раскладывают данные так, чтобы они целиком помещались в L2/L3. Разница в скорости 10–100 раз.
Кто пользуется
Все, у кого больше двух пользователей. Без кэша интернет в текущем виде существовать не может.
- Cloudflare обслуживает около 20% всех веб-сайтов мира; их сеть — это по факту гигантский географически распределённый кэш на десятки петабайт.
- Google. Знаменитый «показать сохранённую копию» — это кэш поисковика. По разным оценкам, индекс Google — сотни петабайт, включая кэшированные версии страниц.
- YouTube/Netflix. У них есть свои edge-серверы, физически стоящие в дата-центрах интернет-провайдеров: топовые видео и сериалы едут не через океан, а из соседней стойки.
- Redis Labs (сейчас Redis Inc.) — стоит в основе кэшей у Twitter, GitHub, Stack Overflow, Airbnb, Amazon. Обрабатывает по разным оценкам >1 млн операций в секунду в крупных инсталляциях.
- Facebook известен своими исследованиями по кэшированию: их система TAO — многоуровневый кэш на десятки миллионов запросов в секунду. Есть открытая статья, если интересно — искать «Facebook TAO cache».
Альтернативы и конкуренты
- Precompute (предвычисление). Не кэшировать по запросу, а один раз в час пересчитать «топ-100 товаров» и раздавать всем готовое. Плюсы: hit rate = 100%, никакой инвалидации. Минусы: не работает для персонализированных данных и для того, чего может не быть в списке заранее.
- Материализованные представления (materialized views). По сути кэш, но на стороне БД: результат сложного запроса хранится как таблица и обновляется по расписанию. Плюсы: нет отдельной инфраструктуры, консистентность внутри БД. Минусы: нагрузка всё равно на БД, обновление может быть дорогим.
- Более быстрый источник. Купить NVMe SSD вместо HDD, поставить больше RAM, переехать на PostgreSQL 16 — иногда проще ускорить сам источник, чем строить перед ним кэш. Плюсы: нет проблемы инвалидации. Минусы: упирается в физику, и обычно дороже кэша по деньгам.
- Горизонтальное масштабирование (шардирование). Разложить данные по 10 серверам вместо одного. Плюсы: масштабируется линейно. Минусы: сложность разработки и эксплуатации в разы выше; кэш проще ввести и убрать.
Когда НЕ стоит использовать
- Данные, которые меняются каждый запрос. Курс акции в реальном времени, счётчик просмотров прямо сейчас, чат-сообщение только что. Кэш всё равно даст промах в 100% случаев, зато добавит один лишний шаг.
- Данные, где отставание недопустимо. Баланс банковского счёта перед списанием, наличие товара на складе в момент оформления заказа, права доступа сразу после смены роли. Здесь любое устаревание — прямой убыток или юридический риск. Кэшировать можно вокруг, но само критическое чтение — только напрямую.
- Персональные данные с индивидуальным ключом. Если у каждого пользователя свой ответ и запросы редкие, hit rate будет околонулевой. Кэш займёт место, но не ускорит ничего. Классическое правило: кэш выгоден, когда одни и те же данные читают много раз.
Связанные понятия
- CDN (Content Delivery Network) — географически распределённый кэш статики; ближайший к пользователю сервер вместо оригинала.
- TTL (time-to-live) — срок жизни записи в кэше; после истечения запись считается устаревшей и обновляется.
- LRU (Least Recently Used) — политика вытеснения: выкидываем то, к чему дольше всего не обращались.
- Memoization — кэширование результатов чистой функции по её аргументам; локальный кэш внутри программы.
- ETag и If-None-Match — заголовки HTTP-кэша: клиент говорит «у меня версия X», сервер отвечает «не менялось» без пересылки тела.
- Cache stampede (стадо) — ситуация, когда кэш протух, и одновременно тысяча клиентов пошли в БД за обновлением. Известная проблема на highload.
Литература и источники
- Wikipedia (ru): «Кэш» — ru.wikipedia.org/wiki/Кэш — базовые понятия, история, типы.
- John Hennessy, David Patterson, «Computer Architecture: A Quantitative Approach» (en, любое издание с 4-го по 6-е) — глава про memory hierarchy. Академический стандарт на 20+ лет.
- Martin Kleppmann, «Designing Data-Intensive Applications» (2017, en; русский перевод «Высоконагруженные приложения», 2018). Кэш разобран в контексте баз и распределённых систем — очень доступным языком.
- Документация Redis — redis.io/docs. Читается по разделам, лучший справочник по практическому кэшированию.
- Документация Cloudflare по кэшированию — developers.cloudflare.com/cache. Отлично объясняет HTTP-заголовки и CDN-механику на живых примерах.
- Статья Facebook TAO — искать в Google запрос «TAO Facebook distributed data store paper». Классический разбор реального кэша на масштабе.
Где встретилось у меня
Вчера в разговоре с ассистентом всплыл кэш локальной системы досье: перед тем как звать платный API за 5 ₽, ассистент проверил, нет ли ответа по этому же запросу в локальной директории. Ответ шестидневной давности нашёлся — платить повторно не пришлось. Это классический cache-aside с человеческой политикой инвалидации («данные месяц не двигаются, свежесть 6 дней норм»). Плюс отдельная история про TTL: агент прямо в интерфейсе показал, что кэшу 6 дней, и предложил обновить за 5 ₽ — правильный паттерн «покажи возраст копии и дай пользователю решить».
Краткое резюме
- Кэш — быстрая копия часто читаемых данных, чтобы не ходить к медленному источнику. Основа скорости всего современного интернета.
- Ключевая проблема — не «положить в кэш», а понять, когда копия устарела (cache invalidation). Знаменито тяжёлая задача.
- Три схемы обновления: TTL (истекает по таймеру), пассивная (проверяем по запросу), активная (источник сам сообщает).
- Метрика здоровья — hit rate. Ниже 70% — кэш почти не окупается; выше 90% — сильно ускоряет систему.
- Не кэшировать: критичные к отставанию данные (баланс, права), уникальные для каждого пользователя запросы без повторов, быстро меняющиеся значения.