При разработке мобильного приложения с миллионной аудиторией узким местом стал API эндпоинт ленты новостей: запрос к PostgreSQL занимал 300–500 мс, а при пиковых нагрузках сервер БД захлёбывался из-за 10 000 запросов в секунду. Мы внедрили Redis-кэширование по паттерну Cache-Aside, и время ответа упало до 2–5 мс, а нагрузка на базу снизилась на 70%. Такая оптимизация значительно сокращает затраты на серверную инфраструктуру — особенно для high-load проектов. Наши инженеры имеют 7+ лет опыта работы с Redis в продакшене, внедрили кэширование для более чем 15 крупных проектов с аудиторией от 1 млн пользователей.
Какие проблемы решаем
Самая частая проблема — thundering herd: при истечении TTL сотни параллельных запросов одновременно идут в БД. Второй типичный сценарий — низкий hit rate (менее 80%), когда кэш почти не используется из-за неправильного выбора данных или TTL. Третья проблема — неконтролируемый рост памяти: без maxmemory Redis может занять весь RAM и упасть.
Мы решаем их с помощью:
-
Защиты от thundering herd: распределённая блокировка через
SET NXили probabilistic early expiration. - Правильного выбора TTL: подбираем под частоту изменений данных (таблица ниже).
-
Ограничения памяти:
maxmemory 512mb, политикаallkeys-lru.
Как мы это делаем: кейс с лентой новостей
Для приложения с 5 миллионами пользователей мы настроили Cache-Aside с версионированием ключей. Ключ формируется как feed:{user_id}:{page}:{version}, где версия меняется при изменении данных (например, при новом посте). Это позволило избежать массовой инвалидации. Для защиты от thundering herd использовали блокировку:
import redis import json r = redis.Redis() def get_news_feed(user_id: int, page: int, version: int): cache_key = f"feed:{user_id}:{page}:{version}" cached = r.get(cache_key) if cached: return json.loads(cached) # Попытка получить блокировку lock_key = f"lock:{cache_key}" if r.setnx(lock_key, 1): r.expire(lock_key, 5) # 5 секунд на генерацию data = db.query_feed(user_id, page) r.setex(cache_key, 300, json.dumps(data)) r.delete(lock_key) return data else: # Ждём и читаем из кэша повторно import time time.sleep(0.1) return get_news_feed(user_id, page, version) После внедрения hit rate поднялся с 60% до 95%, количество evicted_keys упало до нуля.
Почему инвалидация кэша критична?
При прямом обновлении данных в БД кэш остаётся устаревшим до истечения TTL. Это приводит к тому, что пользователи видят неактуальную информацию. Мы используем событийную инвалидацию: при записи в БД отправляется сообщение (через Kafka или Redis Pub/Sub), которое удаляет соответствующие ключи. Для массовой инвалидации применяем SCAN с курсором — это безопасно для продакшена в отличие от KEYS *. Более продвинутый подход — тегированный кэш: каждому объекту присваивается тег, хранящийся в SET. При изменении объекта удаляем все ключи по тегу. Это даёт атомарность и контролируемую инвалидацию.
Подробнее о thundering herd
Паттерн probabilistic early expiration — ещё один метод: за некоторое время до истечения TTL (например, за 10% от TTL) с вероятностью 10% мы регенерируем кэш. Это снижает вероятность одновременного промаха. Redis documentation рекомендует комбинировать блокировку и раннее истечение для максимальной надёжности.
Сравнение паттернов кэширования
| Паттерн | Скорость чтения | Консистентность | Сложность реализации |
|---|---|---|---|
| Cache-Aside | Высокая | Низкая (возможна устаревшая запись) | Низкая |
| Write-Through | Средняя | Высокая (всегда актуальные данные) | Средняя |
| Read-Through | Высокая | Средняя | Высокая (требует Redis modules) |
Как правильно выбрать TTL для кэша
TTL подбирается под частоту изменений:
| Тип данных | Рекомендуемый TTL |
|---|---|
| Конфигурация приложения | 1–24 часа |
| Каталог товаров | 5–30 минут |
| Лента новостей | 1–5 минут |
| Профиль пользователя | 5–15 минут |
Без TTL кэш растёт до упора памяти. Redis при заполнении памяти с политикой allkeys-lru начинает вытеснять ключи — можно потерять важные данные неожиданно. Явный TTL надёжнее.
Почему не стоит использовать KEYS в продакшене
Команда KEYS * блокирует Redis на время выполнения (O(N)), что неприемлемо при высоких нагрузках. Вместо этого используйте SCAN с курсором и DEL каждого найденного ключа. Или теги: каждому ключу приписываем тег в множестве SADD tag:user:42 "feed:42:page:1". При инвалидации — SSCAN тега и удаляем записи. Wikipedia о Redis не даёт таких деталей, но практика подтверждает.
Что входит в работу
- Аудит текущей архитектуры кэширования.
- Проектирование схемы ключей, TTL и защиты от thundering herd.
- Реализация выбранного паттерна (Cache-Aside / Write-Through).
- Настройка мониторинга: hit rate, memory, evictions.
- Документация и обучение команды.
- Поддержка после внедрения.
Сроки: базовая настройка с Cache-Aside для основных эндпоинтов — два-три рабочих дня. Полноценная стратегия с инвалидацией и мониторингом — четыре-семь дней.
Готовы ускорить ваше мобильное приложение? Закажите настройку Redis-кэширования под ключ — наши специалисты оценят ваш проект за один день и предложат оптимальную стратегию. Гарантируем SLA 99.9%. Получите консультацию по внедрению — просто напишите нам.







