🦁 Кэш: этот зверь не так прост, как кажется на первый взгляд
Кэш — механизм, который часто используют как «волшебную кнопку» для ускорения системы — достаточно добавить Redis, и всё работает быстрее. Есть обманчивое восприятие механизма кэширования — будто мы можем радикально изменить производительность системы, снизить затраты на инфраструктуру и даже повлиять на бизнес-метрики.
Почти в каждом проекте наступает момент, когда кто-то говорит: «Давайте просто закэшируем».Обычно после этого система становится быстрее — и одновременно сложнее, опаснее и менее предсказуемой.
Сегодня я хочу рассказать вам о неочевидных на первый взгляд проблемах и подходах к кэшированию.
🚨 То, о чем предпочитают молчать: cache stampede (dog-piling)
Проблема: Есть популярный ресурс с высокой частотой запросов (request rate), который мы кэшируем на определенное время, например TTL 300 секунд. После того как время жизни кэша истекло, все запросы идут в ваш бэкенд, пытаясь обновить кэш.
Решение: Использовать различные подходы обновления кэша, один из них — вероятностное обновление кэша. Идея очень проста: при каждом чтении из кэша мы с определенной вероятностью, например 5% или 10%, обновляем значение, даже если оно не устарело. Это позволит распределить нагрузку по времени и сгладит пики.
❌ Кэширование пустоты — negative caching
Проблема: Ошибки 404, результат неудачной валидации, «пользователь не имеет доступа» — такие ответы статичны и дороги в расчете, а еще могут быть инструментом злоумышленника, чтобы загрузить вашу систему по полной.
Решение: Кэшируйте такие ответы с адекватным TTL (например 30 секунд). Это защитит вашу систему от флуда по несуществующим путям.
⏳ Кэширование с человеческим лицом — stale while revalidate
Проблема: Пользователь ожидает мгновенного отклика, но кэш уже устарел, мы не хотим заставлять его ждать, пока обновим состояние.
Решение: Мы немедленно отдаем ему устаревшие, но валидные данные из кэша, а в фоновом режиме производим его обновление для следующих запросов. Такой подход часто применяется в браузерах для отдачи страниц и данных.
🏗 Многоуровневое кэширование — L1/L2
Сетевой поход в Redis — это быстро, 1-2 миллисекунды, но доступ к оперативной памяти еще быстрее — 10-20 наносекунд. В высоконагруженных системах часто применяют двойное кэширование — кэширование в оперативной памяти для наиболее часто используемых данных (например Caffeine) и распределенный кэш (Redis).
Основная сложность L1/L2 кэширования — как сбросить локальный кэш на нескольких серверах одновременно.
⚡ Сначала обновляем кэш, потом БД — write behind
Проблема: В высокопроизводительных системах с большим числом запросов процедура обновления данных в БД может занимать значительное время. Когда скорость критична, вставка или обновление данных напрямую в БД не всегда подходит.
Решение: При обновлении данных сначала обновляем кэш, а затем в асинхронном режиме применяем изменения в основное хранилище БД. Несколько раз видел такой подход, когда база данных ставится после Redis и обновления происходят сначала в Redis, потом через встроенные стримы обновляется БД. Здесь важно продумать механизмы восстановления данных.
🔮 Предсказательное кэширование — predictive cache
Вместо реактивного кэша (кэшируем после запроса) — пытаемся предсказать поведение системы или пользователя и подготовить кэш заранее. Используя исторические данные о поведении пользователей, можно предсказывать потребность в конкретных ресурсах в тот или иной период времени.
Можно использовать модели машинного обучения для того, чтобы предугадать следующий шаг пользователя и подготовить данные заранее. Такой подход часто используется в рекомендательных системах, персонализированных лентах, поиске.
🎯 Вывод
Кэширование — это искусство баланса между скоростью, consistency и cost. Нестандартные подходы вроде ML-predictive не для каждой системы, но они могут превратить ваш проект из «работает» в «летает».
Что думаете? Поделитесь своими кейсами в комментариях!
Мой канал в telegram