Redis: кэширование, очереди и ограничение скорости

Backend2026-10-05TryQuickToolBox

Вы создали веб-приложение, которое набирает популярность, но с ростом трафика вы замечаете медленные запросы к базе данных и периодические таймауты. Ваш сервер не справляется с нагрузкой, а пользователи жалуются на задержки. Вам нужно быстрое хранилище данных в оперативной памяти, чтобы разгрузить повторяющиеся задачи и справляться с пиками трафика. Redis — это решение для многих задач бэкенда, и в этой статье мы рассмотрим три практических сценария: кэширование, очереди и ограничение скорости.

Почему Redis?

Redis — это открытое хранилище «ключ-значение» в оперативной памяти, известное своей скоростью и универсальностью. Оно поддерживает такие структуры данных, как строки, хэши, списки, множества и сортированные множества, что делает его подходящим для широкого круга задач. В отличие от традиционных баз данных, Redis хранит данные в оперативной памяти, обеспечивая задержку менее миллисекунды. Он также предлагает возможности персистентности, репликации и кластеризации для высокой доступности.

1. Кэширование с Redis

Кэширование — самый распространённый сценарий использования Redis. Сохраняя часто запрашиваемые данные в Redis, вы снижаете нагрузку на основную базу данных и ускоряете время отклика. Например, если ваше приложение отображает список популярных товаров, вы можете кэшировать результат запроса на несколько минут.

Как реализовать кэширование

  1. Проверьте кэш: Перед запросом к базе данных проверьте, есть ли данные в Redis.
  2. Если найдено (попадание в кэш): Верните кэшированные данные.
  3. Если не найдено (промах кэша): Запросите базу данных, сохраните результат в 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