Redis: кэширование, очереди и ограничение скорости
Вы создали веб-приложение, которое набирает популярность, но с ростом трафика вы замечаете медленные запросы к базе данных и периодические таймауты. Ваш сервер не справляется с нагрузкой, а пользователи жалуются на задержки. Вам нужно быстрое хранилище данных в оперативной памяти, чтобы разгрузить повторяющиеся задачи и справляться с пиками трафика. Redis — это решение для многих задач бэкенда, и в этой статье мы рассмотрим три практических сценария: кэширование, очереди и ограничение скорости.
Почему Redis?
Redis — это открытое хранилище «ключ-значение» в оперативной памяти, известное своей скоростью и универсальностью. Оно поддерживает такие структуры данных, как строки, хэши, списки, множества и сортированные множества, что делает его подходящим для широкого круга задач. В отличие от традиционных баз данных, Redis хранит данные в оперативной памяти, обеспечивая задержку менее миллисекунды. Он также предлагает возможности персистентности, репликации и кластеризации для высокой доступности.
1. Кэширование с Redis
Кэширование — самый распространённый сценарий использования Redis. Сохраняя часто запрашиваемые данные в Redis, вы снижаете нагрузку на основную базу данных и ускоряете время отклика. Например, если ваше приложение отображает список популярных товаров, вы можете кэшировать результат запроса на несколько минут.
Как реализовать кэширование
- Проверьте кэш: Перед запросом к базе данных проверьте, есть ли данные в Redis.
- Если найдено (попадание в кэш): Верните кэшированные данные.
- Если не найдено (промах кэша): Запросите базу данных, сохраните результат в Redis с временем жизни (TTL) и верните данные.
Вот пример на Python с использованием redis-py:
import redis
import json
r = redis.Redis(host='localhost', port=6379, db=0)
def get_products():
cache_key = 'products:popular'
cached = r.get(cache_key)
if cached:
return json.loads(cached)
# Simulate database query
products = [{'id': 1, 'name': 'Product A'}, {'id': 2, 'name': 'Product B'}]
r.setex(cache_key, 300, json.dumps(products)) # Cache for 5 minutes
return products
Лучшие практики: Используйте осмысленные ключи кэша, устанавливайте подходящие TTL и продумывайте стратегии инвалидации кэша. Для часто изменяющихся данных используйте короткие TTL или реализуйте сквозное кэширование (write-through).
2. Очереди с Redis
Списки и потоки Redis отлично подходят для создания простых очередей сообщений. Они позволяют отделить длительные задачи (например, отправку электронной почты или обработку изображений) от цикла запрос-ответ, улучшая пользовательский опыт и масштабируемость.
Использование списков в качестве очереди
Вы можете использовать LPUSH для добавления задач в список и BRPOP для атомарного извлечения задач с другого конца. Это создаёт очередь FIFO.
Производитель:
r.lpush('email_queue', json.dumps({'to': 'user@example.com', 'subject': 'Welcome'}))
Потребитель (в рабочем процессе):
while True:
_, job = r.brpop('email_queue', timeout=30)
if job:
process_email(json.loads(job))
Соображения: Списки Redis просты, но не имеют таких продвинутых функций, как подтверждения. Для более надёжных задач рассмотрите Redis Streams, которые поддерживают группы потребителей и подтверждение сообщений.
3. Ограничение скорости с Redis
Ограничение скорости защищает ваш API от злоупотреблений и обеспечивает справедливое использование. Атомарные операции Redis делают его идеальным для реализации ограничителей скорости. Распространённый алгоритм — счётчик скользящего окна, но более простой подход с фиксированным окном также хорошо работает.
Ограничитель скорости с фиксированным окном
Увеличивайте счётчик для каждого пользователя и устанавливайте время жизни. Если счётчик превышает лимит, отклоните запрос.
def is_allowed(user_id, limit=100, window=60):
key = f'rate_limit:{user_id}'
current = r.incr(key)
if current == 1:
r.expire(key, window)
return current