🦁 Кэш: этот зверь не так прост, как кажется на первый взгляд

🦁 Кэш: этот зверь не так прост, как кажется на первый взгляд

Кэш — механизм, который часто используют как «волшебную кнопку» для ускорения системы — достаточно добавить 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

Начать дискуссию