Кэширование: паттерны и инвалидация
Самый частый способ ускорить read-heavy систему — и самый частый источник тонких багов.
Кэш — быстрое хранилище горячих данных (обычно в памяти, например Redis), снимающее нагрузку с основной БД ценой риска отдать устаревшие данные.
Зачем и где
Кэш ставят между приложением и БД, чтобы частые чтения уходили в память, а не в базу. Это резко снижает латентность и QPS на БД. Цена — согласованность: данные в кэше могут устареть.
Клиент -> App -> [Cache] --(промах)--> БД
^----(попадание, быстро)Паттерны записи и чтения
| Паттерн | Суть |
| Cache-aside | App сам читает кэш, при промахе грузит из БД и кладёт в кэш |
| Write-through | Запись идёт в кэш и БД синхронно |
| Write-back | Запись в кэш, в БД — асинхронно (риск потери) |
Cache-aside — дефолт: прост и устойчив. Write-through держит кэш свежим, но замедляет запись. Write-back быстрый, но опасен потерей данных при сбое.
Инвалидация
«В компьютерных науках две сложные вещи: инвалидация кэша и именование». Основные стратегии: TTL (запись живёт N секунд и протухает сама) и явная инвалидация при изменении данных. TTL прост, но допускает окно устаревания; явная инвалидация точнее, но её легко забыть в каком-нибудь пути записи.
Как работает под капотом
Два классических осложнения. Cache stampede: популярный ключ протух, и тысячи запросов одновременно ломятся в БД — лечится блокировкой на пересчёт или вероятностным ранним обновлением. Холодный старт: после рестарта кэш пуст и БД получает всю нагрузку — лечится прогревом. Понимание этих эффектов — сильный сигнал.
Частые ошибки
- Кэшировать всё подряд, включая редкие данные.
- Забыть инвалидацию в одном из путей записи -> вечно устаревшие данные.
- Не предусмотреть stampede на горячих ключах.
Итог
- Кэш ускоряет чтения ценой риска устаревания данных.
- Cache-aside — разумный дефолт; write-through/back — под особые нужды.
- Думайте про TTL, инвалидацию, stampede и холодный старт.