Кэш
Определение и принцип кэширования
Кэш (cache) – промежуточный уровень хранения данных, обеспечивающий быстрый доступ к информации, которая с высокой вероятностью понадобится снова. Эффективность кэша обусловлена двумя фундаментальными свойствами программного кода и данных:
- Временна́я локальность (temporal locality) – данные, к которым обращались недавно, скорее всего понадобятся снова в ближайшее время;
- Пространственная локальность (spatial locality) – данные, расположенные рядом с уже запрошенными, тоже вероятно понадобятся.
Термин произошёл от французского cacher («прятать»). В вычислительной технике кэш-память появилась в конце 1960-х годов: первый коммерческий ЦП с кэшем – IBM System/360 Model 85 (1968) с кэшем ёмкостью 16 КБ при основной памяти до 4 МБ.
Иерархия процессорного кэша: L1, L2, L3
Современные процессоры содержат несколько уровней кэша, образующих иерархию. Каждый уровень компенсирует различие в скорости и объёме между уровнями ниже и выше:
- L1 (Level 1) – встроен непосредственно в ядро, самый быстрый (~1 нс), обычно 32–64 КБ на ядро; разделён на кэш инструкций (I-cache) и кэш данных (D-cache);
- L2 (Level 2) – больше L1 (256 КБ – 1 МБ на ядро), медленнее (~3–10 тактов задержки); у каждого ядра свой L2;
- L3 (Level 3) – разделяемый между всеми ядрами ЦП, ёмкостью 8–64 МБ, задержка ~30–40 тактов.
Скорость L1 может быть до 100 раз выше скорости оперативной памяти, что принципиально важно для производительности вычислительных задач. Промах кэша (cache miss) – ситуация, когда данные отсутствуют в кэше и процессор вынужден обратиться к следующему уровню иерархии памяти.
Политики вытеснения и записи
Кэш имеет ограниченный объём, поэтому при его заполнении требуется алгоритм вытеснения (eviction policy) для удаления устаревших записей:
- LRU (Least Recently Used) – вытесняется строка, к которой дольше всего не было обращений; наиболее распространён;
- LFU (Least Frequently Used) – вытесняется наименее часто используемая строка;
- FIFO (First In, First Out) – вытесняется старейшая по времени загрузки строка;
- Random Replacement – случайное вытеснение; применяется в некоторых аппаратных реализациях из-за простоты.
Политики записи определяют, когда изменённые данные из кэша записываются обратно в основную память:
- Write-through – запись в кэш и основную память одновременно; данные всегда актуальны в памяти, но создаёт дополнительный трафик;
- Write-back (copy-back) – запись только в кэш, в память – при вытеснении строки; эффективнее, но требует отслеживания «грязных» (dirty) строк.
Виды кэширования в программных системах
Концепция кэширования применяется на всех уровнях ИТ-стека:
- DNS-кэш – операционная система и браузер хранят результаты DNS-запросов на время TTL, сокращая задержку разрешения имён;
- Кэш браузера – статические ресурсы (изображения, CSS, JS) сохраняются локально; управляется заголовками HTTP (Cache-Control, ETag, Last-Modified);
- Кэш приложения – Redis, Memcached используются как распределённые in-memory-хранилища для кэширования результатов запросов к БД, вычислений, сессионных данных;
- CDN (Content Delivery Network) – географически распределённый кэш статического и динамического контента на граничных серверах;
- Кэш СУБД – буферный пул (buffer pool) в MySQL/PostgreSQL хранит страницы данных в RAM для ускорения запросов;
- CPU TLB (Translation Lookaside Buffer) – специализированный кэш таблицы трансляции виртуальных адресов в физические.
Проблемы инвалидации кэша и когерентности
Известная поговорка в программировании гласит, что в информатике есть только две сложные задачи: инвалидация кэша и именование переменных. Действительно, управление актуальностью данных в кэше – нетривиальная задача:
- Инвалидация (cache invalidation) – процесс принудительного удаления устаревших данных из кэша при изменении источника;
- Когерентность кэша (cache coherence) – в многопроцессорных системах данные одной ячейки памяти могут быть одновременно в L1-кэшах нескольких ядер; протоколы MESI, MOESI обеспечивают их согласованность;
- Cache stampede (thundering herd) – при истечении TTL множество запросов одновременно обращаются к источнику данных, создавая пиковую нагрузку.
Стратегии борьбы с проблемами кэширования: TTL с джиттером, cache-aside pattern, read-through/write-through паттерны, distributed locking при обновлении.
Метрики эффективности кэша
Ключевая метрика – hit rate (доля обращений, обслуженных из кэша). Для CPU-кэшей целевой hit rate составляет 95–99%. Для распределённого application-кэша приемлемый hit rate зависит от стоимости промаха: если запрос к БД занимает 100 мс, а кэш-hit – 1 мс, то hit rate 90% означает среднюю задержку ~11 мс вместо 100 мс. Мониторинг hit/miss ratio и eviction rate является обязательной частью операционного наблюдения за системами с кэшированием.