С нуля: что такое кэш на самом деле
Кэш — это быстрая копия дорогостоящих в получении данных, расположенная рядом. Это карта «с нуля» и восемь слов, которые остальной трек о кэшировании считает уже знакомыми.
Твоё приложение тормозит. Пользователь кликает «Загрузить профиль» и ждёт две секунды, пока сервер делает запрос в базу, джойнит пять таблиц и сериализует JSON-блоб — каждый раз, для каждого пользователя, даже если данные почти не меняются. А теперь представь, что сервер мог бы отвечать за две миллисекунды, держа готовый ответ рядом. Это и есть кэш. Один из старейших трюков в вычислениях — процессоры делают это с 1960-х — и при этом источник одной из самых известных фраз в программировании: «В информатике есть только две сложные вещи: инвалидация кэша и придумывание имён». Этот урок — карта этих двух сложных вещей до того, как ты заберёшься в детали.
Единственная проблема, которую решает кэш
Каждая полезная программа рано или поздно упирается в одну стену: получать данные — медленно. Возможно, это требует обращения к базе данных. Возможно, сетевого вызова к стороннему API. Возможно, ресурсоёмкого вычисления. Какова бы ни была причина, стоимость реальна — и если многие пользователи запрашивают одни и те же данные, ты платишь её снова и снова за один и тот же результат. Кэш разрывает это повторение. Вместо того чтобы каждый раз возвращаться к исходному источнику, ты сохраняешь копию где-то быстрее и ближе к читателю. Когда ты профилируешь медленный эндпоинт и видишь, что одна и та же строка из базы запрашивается сотни раз в секунду, — это и есть та стена, для которой создан кэш. Следующий запрос находит эту копию и завершается до того, как дорогостоящий путь вообще начался.
Всё остальное в этом треке — уточнение одной идеи: держи быструю копию рядом, но держи её честной.
Восемь слов, которые остальной трек считает знакомыми
Можно сразу нырять в политики вытеснения Redis или заголовки Cache-Control у CDN — но каждая из этих тем будет использовать эти восемь слов, не делая паузы на объяснения. Разберись с ними здесь один раз, и остальной трек будет ощущаться как узнавание, а не загадка.
Senior-уроки дальше используют эти термины, не останавливаясь на определениях. Вот они, по одному предложению — что это и зачем оно.
| Слово | Что это | Зачем оно |
|---|---|---|
| Кэш | Быстрая копия данных поблизости, дорогостоящих для получения из источника. | Чтобы повторные чтения платили высокую стоимость один раз, а не каждый раз. |
| Cache hit (попадание) | Момент, когда запрос находит данные уже в кэше. | Быстрый путь — поход к origin не нужен, ответ возвращается немедленно. |
| Cache miss (промах) | Момент, когда запрос не находит ничего полезного в кэше. | Запускает медленный путь — fetch из origin, сохранение результата, выдача копии. |
| TTL (time-to-live) | Обратный отсчёт у каждой записи; когда доходит до нуля — запись истекает. | Чтобы устаревшие данные не жили вечно — кэш в итоге обновляет себя сам. |
| Eviction (вытеснение) | Удаление записи до истечения TTL — обычно из-за нехватки места. | Чтобы кэш оставался в пределах бюджета памяти, выбрасывая наименее ценные записи. |
| Staleness (устарелость) | Состояние кэшированной копии, которая больше не совпадает с текущим значением в origin. | Понимание устарелости отделяет кэш, который помогает, от кэша, который вводит в заблуждение. |
| Cache key (ключ кэша) | Идентификатор для хранения и поиска записи — часто URL, запрос или ID пользователя. | Чтобы кэш различал «профиль пользователя 42» и «профиль пользователя 99». |
| Origin (источник истины) | Авторитетный источник, копией которого является кэш — обычно база или upstream API. | Чтобы всегда было место, откуда получить свежие данные, когда кэш не может помочь. |
| Invalidation (инвалидация) | Намеренное удаление или пометка устаревшей записи при изменении данных в origin. | Чтобы после записи пользователи не читали устаревшие данные — самая трудная часть кэширования. |
Как они складываются вместе
Прочитанные по порядку, слова рассказывают одну историю: когда приходит запрос, проверь кэш по cache key. Если есть cache hit — верни копию немедленно, быстрый путь завершён. Если cache miss — fetch данных из origin, сохрани их в кэше под тем же ключом с TTL и верни вызывающей стороне. Следующий запрос с тем же ключом — попадание. Когда TTL истекает, запись становится stale и снова считается промахом. Если память заканчивается, eviction удаляет записи, которые кэш считает наименее ценными. А когда данные в origin меняются, invalidation удаляет старую копию, чтобы следующее чтение взяло свежую. Этот абзац — весь трек в миниатюре; каждый следующий юнит увеличивает один из шагов.
▸Почему это работает
Почему просто не читать всегда из origin? Потому что origin медленный и часто дорогой. Запрос к базе за 50 мс звучит нормально, пока тысяча пользователей не задаёт одинаковый вопрос в секунду — теперь тебе нужна тысяча запросов в секунду. Хорошо расположенный кэш превращает это в один запрос к базе за TTL и 999 мгновенных попаданий. Цена — сложность: теперь у тебя два источника правды и ты должен держать их в синхронизации. Эта проблема синхронизации — инвалидация — и есть причина, по которой кэширование известно своей сложностью.
Это не нужно зубрить
Две честные ремарки перед восхождением. Первая: никто не держит всё это в голове сразу в первый день — ты встретишь каждое слово снова, в глубине, в своём юните, и тогда оно уляжется. Эта страница — вешалка, на которую вешать детали, а не экзамен. Вторая: кэши существуют на каждом уровне системы — от L1-кэша процессора на расстоянии нескольких наносекунд до CDN edge-узла на расстоянии нескольких миллисекунд — и словарь выше применим ко всем им. Senior-трек проходит каждый уровень. Начни здесь, и каждый следующий уровень будет ощущаться знакомым.
Пользователь запрашивает страницу товара. В кэше есть запись, но TTL истёк десять секунд назад. Что происходит?
Расставь жизненный цикл: cache miss превращается в будущий hit:
- 1 Приходит запрос — проверяем кэш по ключу, ничего не находим (miss)
- 2 Получаем свежие данные из origin
- 3 Сохраняем результат в кэше под тем же ключом с TTL
- 4 Возвращаем данные вызывающей стороне; следующий такой запрос — hit
- 01В одном дыхании: что такое кэш и в чём его ключевой компромисс?
- 02Опиши, что происходит при cache miss, назвав каждую составляющую.
Кэш — это одна идея с кучей навешанной механики: держи быструю копию дорогостоящих данных рядом, чтобы повторные чтения были дешёвыми. Каждый запрос сначала проверяет кэш по ключу — попадание возвращает данные немедленно, промах идёт к origin и пополняет кэш с TTL. Когда TTL истекает, запись считается stale и снова стаёт промахом; когда память заканчивается, eviction освобождает место, выбрасывая наименее ценные записи. Сложная часть — инвалидация: когда origin меняется, устаревшие копии должны быть удалены или обновлены, чтобы читатели оставались актуальными. Каждое из этих слов ты встретишь в глубине позже — это просто карта. Теперь, когда встретишь медленный эндпоинт, первый вопрос должен быть: есть ли копия ближе к читателю — и если нет, что потребуется, чтобы её добавить?
Что-то непонятно?
Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.