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

Backend2026-09-15TryQuickToolBox

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

Почему Redis?

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

1. Кэширование с помощью Redis

Кэширование — самый распространённый вариант использования Redis. Идея проста: сохранять результаты дорогостоящих операций (например, запросов к базе данных или вызовов API) в Redis и отдавать их напрямую для последующих запросов. Это снижает нагрузку на основную базу данных и ускоряет время отклика.

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

  1. Проверьте кэш: перед обращением к базе данных запросите данные из Redis по уникальному ключу (например, user:123).
  2. Если найдено (попадание в кэш): немедленно верните кэшированные данные.
  3. Если не найдено (промах кэша): извлеките данные из базы данных, сохраните их в Redis с временем жизни (TTL) и верните.

Вот пример на Python с использованием redis-py:

import redis
import json

r = redis.Redis(host='localhost', port=6379, db=0)

def get_user(user_id):
    cache_key = f'user:{user_id}'
    cached = r.get(cache_key)
    if cached:
        return json.loads(cached)
    # Simulate database fetch
    user = fetch_user_from_db(user_id)
    r.setex(cache_key, 3600, json.dumps(user))  # Cache for 1 hour
    return user

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

2. Очереди с помощью Redis

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

Реализация базовой очереди

Используйте LPUSH, чтобы добавлять задания в левую часть списка, и BRPOP, чтобы блокироваться и извлекать из правой. Это создаёт очередь FIFO.

# Producer: add job
r.lpush('email_queue', json.dumps({'to': 'user@example.com', 'subject': 'Welcome'}))

# Consumer: process jobs
while True:
    _, job = r.brpop('email_queue', timeout=30)
    if job:
        process_email(json.loads(job))

Для более сложных задач Redis Streams предоставляют группы потребителей, подтверждения и возможность повторного воспроизведения, что делает их подходящими для надёжной обработки сообщений.

3. Ограничение скорости с помощью Redis

Ограничение скорости защищает ваш API от злоупотреблений и обеспечивает справедливое использование. Атомарные операции Redis упрощают реализацию счётчиков, которые автоматически истекают.

Ограничение скорости с фиксированным окном

При подходе с фиксированным окном вы разрешаете N запросов за временной интервал (например, 100 запросов в час на IP). Используйте INCR и EXPIRE:

def is_allowed(ip, limit=100, window=3600):
    key = f'rate:{ip}'
    current = r.incr(key)
    if current == 1:
        r.expire(key, window)
    return current