🔴

Redis — Полное руководство

In-memory база данных для кеширования, очередей сообщений, real-time аналитики. Redis Queue, Celery интеграция, Redis Streams — всё с примерами на Python.

In-Memory Caching Queues Pub/Sub Streams
2009
Год создания
1M+
Ops/sec
<1ms
Latency
64K+
GitHub Stars

🔴 Что такое Redis

Redis (Remote Dictionary Server) — это высокопроизводительная in-memory база данных, хранящая данные в оперативной памяти. Это делает её невероятно быстрой — миллионы операций в секунду.

⚡ Почему Redis такой быстрый?

RAM vs Disk: Чтение из RAM ~100 наносекунд, с диска ~10 миллисекунд. Разница в 100 000 раз!

Однопоточность: Нет накладных расходов на блокировки и синхронизацию.

Простые структуры: Оптимизированные алгоритмы для каждого типа данных.

Ключевые особенности

  • In-Memory — данные в RAM, скорость <1ms
  • Персистентность — опционально сохраняет на диск (RDB, AOF)
  • Репликация — Master-Replica для отказоустойчивости
  • Кластеризация — Redis Cluster для горизонтального масштабирования
  • Pub/Sub — встроенная система публикации/подписки
  • Lua scripting — атомарные операции через скрипты
  • TTL — автоматическое удаление по времени жизни
⚠️ Redis — не замена основной БД

Redis хранит данные в RAM — это дорого и ограничено. Используйте Redis как дополнение к PostgreSQL/MySQL:

• Основные данные → PostgreSQL (надёжность)
• Кеш, сессии, очереди → Redis (скорость)

📊 Типы данных Redis

Redis — не просто key-value хранилище. Он поддерживает богатые структуры данных, каждая оптимизирована для своих задач:

📝
String
Базовый тип. Текст, числа, бинарные данные до 512MB.
SET user:1:name "John"
GET user:1:name → "John"
INCR page:views → 1, 2, 3...
📋
List
Связный список строк. Очереди, последние N элементов.
LPUSH queue "task1"
RPOP queue → "task1"
LRANGE list 0 9 → последние 10
🎯
Set
Неупорядоченное множество уникальных строк.
SADD tags "python" "redis"
SISMEMBER tags "python" → 1
SINTER tags1 tags2 → пересечение
🏆
Sorted Set
Множество с весом (score). Рейтинги, leaderboards.
ZADD leaderboard 100 "player1"
ZRANK leaderboard "player1" → 0
ZRANGE leaderboard 0 9 → топ-10
🗂️
Hash
Словарь field-value. Объекты, профили пользователей.
HSET user:1 name "John" age 30
HGET user:1 name → "John"
HGETALL user:1 → все поля
🌊
Stream
Append-only лог. Event sourcing, очереди сообщений.
XADD events * action "click"
XREAD STREAMS events 0
XRANGE events - +
📍
Geo
Геопространственные данные. Поиск по радиусу.
GEOADD locations 13.36 52.52 "Berlin"
GEODIST locations "Berlin" "Paris"
GEORADIUS locations 13 52 100 km
🎲
HyperLogLog
Вероятностный подсчёт уникальных элементов. ~0.81% погрешность.
PFADD visitors "user1" "user2"
PFCOUNT visitors → ~2
# 12KB на миллиарды элементов!

🎯 Где использовать Redis

Кеширование
Самый популярный сценарий. Кешируйте результаты запросов к БД, API-ответы, HTML-фрагменты. Снижение нагрузки на БД в 10-100 раз.
🔐
Сессии пользователей
Хранение сессий для stateless приложений. Быстрый доступ, автоматическое истечение через TTL.
📬
Очереди задач
Отложенные задачи, фоновая обработка. Redis Queue, Celery, Bull — все используют Redis.
📊
Real-time аналитика
Счётчики просмотров, лайков, онлайн-пользователей. INCR атомарен и супер-быстрый.
🏆
Лидерборды / Рейтинги
Sorted Set идеально подходит для рейтингов. Топ-N игроков, сортировка по очкам за O(log N).
🔔
Pub/Sub уведомления
Real-time уведомления, чаты, live-обновления. Встроенный механизм публикации/подписки.
🚦
Rate Limiting
Ограничение запросов: 100 req/min на пользователя. INCR + EXPIRE = простой rate limiter.
🔒
Распределённые блокировки
Координация между серверами. Redlock — алгоритм распределённых блокировок.

🚀 Быстрый старт

Установка через Docker (рекомендуется)

Terminal
# Запуск Redis в Docker
$ docker run -d --name redis -p 6379:6379 redis:alpine
# С персистентностью данных
$ docker run -d --name redis -p 6379:6379 -v redis-data:/data redis:alpine redis-server --appendonly yes
# Подключение к Redis CLI
$ docker exec -it redis redis-cli

Базовые команды Redis CLI

redis-cli
# Проверка соединения
127.0.0.1:6379> PING
PONG
# String: установить и получить
127.0.0.1:6379> SET greeting "Hello, Redis!"
OK
127.0.0.1:6379> GET greeting
"Hello, Redis!"
# TTL: ключ с временем жизни (60 сек)
127.0.0.1:6379> SETEX session:abc123 60 "user:1"
OK
127.0.0.1:6379> TTL session:abc123
(integer) 58
# Счётчик
127.0.0.1:6379> INCR page:views
(integer) 1
127.0.0.1:6379> INCRBY page:views 10
(integer) 11
# Hash: объект пользователя
127.0.0.1:6379> HSET user:1 name "John" email "john@example.com" age 30
(integer) 3
127.0.0.1:6379> HGETALL user:1
1) "name" 2) "John" 3) "email" 4) "john@example.com" 5) "age" 6) "30"

🐍 Redis + Python

Terminal — Установка
$ pip install redis
Python redis_basics.py
import redis
import json

# Подключение к Redis
r = redis.Redis(
    host='localhost',
    port=6379,
    db=0,
    decode_responses=True  # Автоматически декодировать в str
)

# ============ Strings ============

# Простое значение
r.set('greeting', 'Hello, Redis!')
print(r.get('greeting'))  # "Hello, Redis!"

# С временем жизни (TTL)
r.setex('session:abc123', 3600, 'user:1')  # 1 час
print(r.ttl('session:abc123'))  # 3599

# Счётчики
r.set('views', 0)
r.incr('views')       # 1
r.incrby('views', 10) # 11

# ============ Hash (объекты) ============

# Сохранить объект пользователя
r.hset('user:1', mapping={
    'name': 'John Doe',
    'email': 'john@example.com',
    'age': '30'
})

# Получить все поля
user = r.hgetall('user:1')
print(user)  # {'name': 'John Doe', 'email': '...', 'age': '30'}

# Одно поле
print(r.hget('user:1', 'name'))  # "John Doe"

# ============ List (очередь) ============

# Добавить в очередь
r.lpush('tasks', 'task1', 'task2', 'task3')

# Взять из очереди (FIFO)
task = r.rpop('tasks')
print(task)  # "task1"

# Блокирующее ожидание (для воркеров)
# task = r.brpop('tasks', timeout=30)  # Ждать до 30 сек

# ============ Set (уникальные значения) ============

r.sadd('tags:post:1', 'python', 'redis', 'tutorial')
r.sadd('tags:post:2', 'python', 'django')

# Пересечение тегов
common = r.sinter('tags:post:1', 'tags:post:2')
print(common)  # {'python'}

# ============ Sorted Set (рейтинг) ============

# Добавить игроков с очками
r.zadd('leaderboard', {'player1': 100, 'player2': 200, 'player3': 150})

# Топ-3 (по убыванию)
top3 = r.zrevrange('leaderboard', 0, 2, withscores=True)
print(top3)  # [('player2', 200.0), ('player3', 150.0), ('player1', 100.0)]

# Ранг игрока
rank = r.zrevrank('leaderboard', 'player1')
print(f"Player1 rank: {rank + 1}")  # 3

Паттерн: Кеширование с Redis

Python cache_pattern.py
import redis
import json
from functools import wraps

r = redis.Redis(host='localhost', decode_responses=True)

# ============ Декоратор для кеширования ============

def cache(ttl=300):
    """Кеширует результат функции на ttl секунд"""
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            # Генерируем ключ из имени функции и аргументов
            key = f"cache:{func.__name__}:{args}:{kwargs}"

            # Пробуем получить из кеша
            cached = r.get(key)
            if cached:
                print(f"✓ Cache HIT: {key}")
                return json.loads(cached)

            # Нет в кеше — вызываем функцию
            print(f"✗ Cache MISS: {key}")
            result = func(*args, **kwargs)

            # Сохраняем в кеш
            r.setex(key, ttl, json.dumps(result))
            return result
        return wrapper
    return decorator

# ============ Использование ============
@cache(ttl=60)
def get_user_from_db(user_id):
    """Имитация тяжёлого запроса к БД"""
    import time
    time.sleep(2)  # Типа долгий запрос
    return {
        'id': user_id,
        'name': f'User {user_id}',
        'email': f'user{user_id}@example.com'
    }

# Первый вызов — 2 секунды (запрос к "БД")
user = get_user_from_db(1)  # ✗ Cache MISS

# Второй вызов — мгновенно (из кеша)
user = get_user_from_db(1)  # ✓ Cache HIT

# ============ Cache-Aside Pattern ============

def get_product(product_id):
    """Классический паттерн Cache-Aside"""
    cache_key = f"product:{product_id}"

    # 1. Проверяем кеш
    cached = r.get(cache_key)
    if cached:
        return json.loads(cached)

    # 2. Запрос к БД
    product = db.query("SELECT * FROM products WHERE id = %s", product_id)

    if product:
        # 3. Сохраняем в кеш
        r.setex(cache_key, 3600, json.dumps(product))

    return product

def update_product(product_id, data):
    """При обновлении — инвалидируем кеш"""
    # 1. Обновляем в БД
    db.query("UPDATE products SET ... WHERE id = %s", product_id)

    # 2. Удаляем из кеша
    r.delete(f"product:{product_id}")

📬 Redis Queue (RQ)

RQ (Redis Queue) — простая библиотека для фоновых задач на Python. Легче Celery, отлично подходит для небольших и средних проектов.

Архитектура Redis Queue
🌐 Web App
🔴 Redis
Queue
⚙️ Worker 1
⚙️ Worker 2
⚙️ Worker N
Terminal — Установка
$ pip install rq

Шаг 1: Определяем задачи

Python tasks.py
import time
import requests

def send_email(to, subject, body):
    """Отправка email (тяжёлая операция)"""
    print(f"📧 Sending email to {to}...")
    time.sleep(3)  # Имитация отправки
    print(f"✅ Email sent to {to}")
    return {'status': 'sent', 'to': to}

def process_image(image_url):
    """Обработка изображения"""
    print(f"🖼️ Processing {image_url}...")
    time.sleep(5)  # Тяжёлая обработка
    return {'status': 'processed', 'url': image_url}

def generate_report(user_id, date_range):
    """Генерация PDF-отчёта"""
    print(f"📊 Generating report for user {user_id}...")
    time.sleep(10)
    return {'status': 'ready', 'file': f'/reports/{user_id}.pdf'}

Шаг 2: Добавляем задачи в очередь

Python app.py
from redis import Redis
from rq import Queue
from tasks import send_email, process_image, generate_report

# Подключение к Redis
redis_conn = Redis(host='localhost', port=6379)

# Создаём очереди
default_queue = Queue(connection=redis_conn)
high_priority = Queue('high', connection=redis_conn)
low_priority = Queue('low', connection=redis_conn)

# ============ Добавление задач ============

# Простое добавление
job = default_queue.enqueue(send_email, 
    'user@example.com', 
    'Welcome!', 
    'Hello from our app'
)
print(f"Job ID: {job.id}")

# С приоритетом
urgent_job = high_priority.enqueue(send_email,
    'vip@example.com',
    'Urgent!',
    'Important message'
)

# Отложенная задача (через 60 секунд)
from datetime import timedelta
delayed_job = default_queue.enqueue_in(
    timedelta(seconds=60),
    send_email,
    'user@example.com',
    'Reminder',
    'Don\'t forget!'
)

# С таймаутом выполнения
long_job = low_priority.enqueue(
    generate_report,
    'user123',
    '2024-01-01:2024-12-31',
    job_timeout=600  # 10 минут максимум
)

# ============ Проверка статуса ============

print(f"Job status: {job.get_status()}")  # queued, started, finished, failed
print(f"Job result: {job.result}")  # None пока не выполнено

# Дождаться результата (блокирующий вызов)
result = job.result
if result is None:
    job.refresh()  # Обновить статус
    if job.is_finished:
        result = job.result

Шаг 3: Запускаем Worker

Terminal — Worker
# Запуск воркера (слушает default очередь)
$ rq worker
14:23:45 Worker rq:worker:abc123 started 14:23:45 Listening on default... 14:23:47 default: tasks.send_email('user@example.com', ...) 📧 Sending email to user@example.com... ✅ Email sent to user@example.com 14:23:50 default: Job OK (3.01s)
# Воркер с несколькими очередями (приоритет важен!)
$ rq worker high default low
# Несколько воркеров (масштабирование)
$ rq worker & rq worker & rq worker
💡 RQ Dashboard

Веб-интерфейс для мониторинга очередей:

pip install rq-dashboard
rq-dashboard

Откройте http://localhost:9181 — увидите все очереди, задачи, воркеры.

🥬 Redis + Celery

Celery — мощная система распределённых очередей задач. Сложнее RQ, но больше возможностей: периодические задачи, цепочки, группы, retry, мониторинг.

📬
RQ vs Celery
Когда что выбрать?
  • RQ Простые проекты, быстрый старт
  • RQ Только Python
  • Celery Сложная логика, цепочки задач
  • Celery Периодические задачи (cron)
  • Celery Несколько брокеров (Redis, RabbitMQ)
Возможности Celery
Что умеет?
  • Асинхронные задачи
  • Периодические задачи (Celery Beat)
  • Retry с exponential backoff
  • Цепочки, группы, хорды
  • Мониторинг (Flower)
Terminal — Установка
$ pip install celery[redis]

Конфигурация Celery

Python celery_app.py
from celery import Celery

# Создаём приложение Celery
app = Celery(
    'myapp',
    broker='redis://localhost:6379/0',      # Брокер (очередь задач)
    backend='redis://localhost:6379/1',     # Backend (результаты)
    include=['tasks']                        # Модули с задачами
)

# Конфигурация
app.conf.update(
    task_serializer='json',
    result_serializer='json',
    accept_content=['json'],
    timezone='UTC',
    enable_utc=True,

    # Retry настройки
    task_acks_late=True,
    task_reject_on_worker_lost=True,

    # Таймауты
    task_soft_time_limit=300,   # 5 минут soft limit
    task_time_limit=600,        # 10 минут hard limit
)

# Периодические задачи (Celery Beat)
app.conf.beat_schedule = {
    'cleanup-every-hour': {
        'task': 'tasks.cleanup_old_data',
        'schedule': 3600.0,  # каждый час
    },
    'daily-report': {
        'task': 'tasks.generate_daily_report',
        'schedule': crontab(hour=9, minute=0),  # каждый день в 9:00
    },
}

Определение задач

Python tasks.py
from celery_app import app
import time

# ============ Базовая задача ============

@app.task
def send_email(to, subject, body):
    """Простая задача отправки email"""
    print(f"📧 Sending to {to}")
    time.sleep(2)
    return {'status': 'sent', 'to': to}

# ============ Задача с retry ============

@app.task(
    bind=True,
    autoretry_for=(Exception,),
    retry_backoff=True,        # Exponential backoff
    retry_backoff_max=600,     # Max 10 минут между retry
    max_retries=5
)
def call_external_api(self, url):
    """Задача с автоматическим retry при ошибках"""
    try:
        response = requests.get(url, timeout=10)
        response.raise_for_status()
        return response.json()
    except requests.RequestException as exc:
        print(f"❌ API error: {exc}, retry {self.request.retries}/{self.max_retries}")
        raise  # autoretry сработает

# ============ Задача с ручным retry ============

@app.task(bind=True, max_retries=3)
def process_payment(self, payment_id):
    """Обработка платежа с контролем retry"""
    try:
        result = payment_gateway.process(payment_id)
        return result
    except PaymentError as exc:
        # Retry через 60 секунд
        raise self.retry(exc=exc, countdown=60)

# ============ Периодические задачи ============

@app.task
def cleanup_old_data():
    """Очистка старых данных (запускается по расписанию)"""
    deleted = db.execute("DELETE FROM logs WHERE created_at < NOW() - INTERVAL '30 days'")
    return {'deleted_rows': deleted}

@app.task
def generate_daily_report():
    """Ежедневный отчёт"""
    # ... генерация отчёта
    return {'status': 'generated'}

Вызов задач

Python views.py
from tasks import send_email, call_external_api

# ============ Асинхронный вызов ============

# Отправить в очередь (не ждём результат)
result = send_email.delay('user@example.com', 'Hello', 'Welcome!')
print(f"Task ID: {result.id}")

# Альтернативный синтаксис
result = send_email.apply_async(
    args=['user@example.com', 'Hello', 'Welcome!'],
    countdown=60,      # Выполнить через 60 сек
    expires=3600,      # Задача истекает через час
    queue='high'       # Конкретная очередь
)

# ============ Проверка статуса ============

print(result.status)      # PENDING, STARTED, SUCCESS, FAILURE
print(result.ready())     # True если завершено
print(result.successful())# True если успешно

# Получить результат (блокирующий вызов)
try:
    data = result.get(timeout=10)  # Ждать макс 10 сек
    print(f"Result: {data}")
except TimeoutError:
    print("Task still running...")

# ============ Цепочки задач ============

from celery import chain, group, chord

# Последовательное выполнение
workflow = chain(
    download_file.s('http://example.com/data.csv'),
    parse_csv.s(),
    save_to_db.s()
)
result = workflow.apply_async()

# Параллельное выполнение
emails = group(
    send_email.s('user1@example.com', 'Hi', 'Body'),
    send_email.s('user2@example.com', 'Hi', 'Body'),
    send_email.s('user3@example.com', 'Hi', 'Body'),
)
result = emails.apply_async()

Запуск Celery

Terminal — Celery Workers
# Запуск воркера
$ celery -A celery_app worker --loglevel=info
# Воркер с конкурентностью
$ celery -A celery_app worker -c 4 --loglevel=info
# Воркер для конкретной очереди
$ celery -A celery_app worker -Q high,default --loglevel=info
# Celery Beat (планировщик периодических задач)
$ celery -A celery_app beat --loglevel=info
# Flower — веб-мониторинг
$ pip install flower
$ celery -A celery_app flower --port=5555
# Откройте http://localhost:5555

🌊 Redis Streams

Redis Streams — структура данных для event streaming и обработки сообщений. Как Apache Kafka, но встроенный в Redis. Идеально для event-driven архитектуры.

📊 Streams vs Pub/Sub vs List

Pub/Sub: Fire-and-forget. Если подписчика нет — сообщение потеряно.

List: Простые очереди. Один consumer = одно сообщение.

Streams: Персистентный лог. Consumer Groups, acknowledgments, история сообщений. Надёжность + масштабируемость.

Redis Stream — append-only лог
1699999999999-0
user: "john", action: "login"
1699999999999-1
user: "jane", action: "purchase"
1700000000000-0
user: "john", action: "logout"
...
новые события

Базовые операции

redis-cli — Streams
# Добавить событие в stream (* = автогенерация ID)
127.0.0.1:6379> XADD events * user john action login ip 192.168.1.1
"1700000000000-0"
# Ещё событие
127.0.0.1:6379> XADD events * user jane action purchase amount 99.99
"1700000000001-0"
# Прочитать все события
127.0.0.1:6379> XRANGE events - +
1) 1) "1700000000000-0" 2) 1) "user" 2) "john" 3) "action" 4) "login" ... 2) 1) "1700000000001-0" 2) 1) "user" 2) "jane" 3) "action" 4) "purchase" ...
# Длина stream
127.0.0.1:6379> XLEN events
(integer) 2

Consumer Groups

Consumer Groups позволяют нескольким воркерам обрабатывать один stream, при этом каждое сообщение получает только один воркер:

Consumer Groups
📝 Producer
🌊 Stream
"events"
👥 Group
"analytics"
Consumer 1
Consumer 2
👥 Group
"notifications"
Consumer 3

Python + Redis Streams

Python streams_producer.py
import redis
import json
from datetime import datetime

r = redis.Redis(host='localhost', decode_responses=True)

# ============ Producer: отправка событий ============

def publish_event(stream_name, event_type, data):
    """Публикация события в stream"""
    event = {
        'type': event_type,
        'timestamp': datetime.utcnow().isoformat(),
        'data': json.dumps(data)
    }

    # XADD добавляет в stream, * = автогенерация ID
    message_id = r.xadd(stream_name, event)
    print(f"✅ Published: {message_id}")
    return message_id

# Примеры событий
publish_event('user-events', 'user.registered', {
    'user_id': 123,
    'email': 'john@example.com'
})

publish_event('user-events', 'user.login', {
    'user_id': 123,
    'ip': '192.168.1.1'
})

publish_event('order-events', 'order.created', {
    'order_id': 456,
    'user_id': 123,
    'total': 99.99
})
Python streams_consumer.py
import redis
import json

r = redis.Redis(host='localhost', decode_responses=True)

STREAM = 'user-events'
GROUP = 'analytics-group'
CONSUMER = 'consumer-1'

# ============ Создание Consumer Group ============

def create_consumer_group():
    """Создать группу (если не существует)"""
    try:
        # $ = читать только новые сообщения
        # 0 = читать все с начала
        r.xgroup_create(STREAM, GROUP, id='0', mkstream=True)
        print(f"✅ Group '{GROUP}' created")
    except redis.exceptions.ResponseError as e:
        if 'BUSYGROUP' in str(e):
            print(f"ℹ️ Group '{GROUP}' already exists")
        else:
            raise

# ============ Consumer: обработка событий ============

def process_events():
    """Основной цикл обработки событий"""
    print(f"🚀 Consumer '{CONSUMER}' started, listening to '{STREAM}'...")

    while True:
        # Читаем сообщения из группы
        # > = только новые (не прочитанные этой группой)
        messages = r.xreadgroup(
            groupname=GROUP,
            consumername=CONSUMER,
            streams={STREAM: '>'},
            count=10,        # Максимум 10 за раз
            block=5000       # Ждать 5 сек если нет сообщений
        )

        if not messages:
            continue

        for stream, events in messages:
            for message_id, data in events:
                try:
                    # Обработка события
                    process_single_event(message_id, data)

                    # Подтверждаем обработку (ACK)
                    r.xack(STREAM, GROUP, message_id)
                    print(f"✅ ACK: {message_id}")

                except Exception as e:
                    print(f"❌ Error processing {message_id}: {e}")
                    # Не делаем ACK — сообщение останется в pending

def process_single_event(message_id, data):
    """Обработка одного события"""
    event_type = data.get('type')
    payload = json.loads(data.get('data', '{}'))

    print(f"📨 Processing: {event_type}")
    print(f"   Data: {payload}")

    if event_type == 'user.registered':
        # Отправить welcome email
        send_welcome_email(payload['email'])
    elif event_type == 'user.login':
        # Записать в аналитику
        track_login(payload['user_id'], payload['ip'])

if __name__ == '__main__':
    create_consumer_group()
    process_events()
💡 Когда использовать Streams

Event Sourcing — лог всех изменений в системе
Activity Feed — лента активности пользователей
IoT данные — поток данных с датчиков
Audit Log — журнал аудита действий
Микросервисы — асинхронная коммуникация между сервисами

Best Practices

🔑
Именование ключей
Используйте namespace с разделителем
  • user:123:profile
  • cache:products:456
  • session:abc123
  • queue:emails:high
Всегда устанавливайте TTL
Избегайте утечек памяти
  • Сессии: 24 часа
  • Кеш: 5-60 минут
  • Rate limit: 1 минута
  • Временные данные: по задаче
📦
Сериализация данных
Выбирайте формат с умом
  • JSON Читаемость, совместимость
  • MessagePack Компактность, скорость
  • Pickle Python-only, осторожно!
🔒
Безопасность
Защитите свой Redis
  • Пароль (requirepass)
  • Bind только localhost
  • Firewall правила
  • TLS для продакшена

Чего избегать

⚠️ Антипаттерны Redis

❌ KEYS * — блокирует Redis, используйте SCAN

❌ Большие значения — >100KB замедляют Redis

❌ Без TTL — память заполнится, Redis упадёт

❌ Один ключ для всего — разбивайте данные

❌ Хранить всё в Redis — это кеш, не основная БД

Memory Management

Redis Config redis.conf
# Максимальная память
maxmemory 2gb

# Политика вытеснения при нехватке памяти
# volatile-lru  — удалять ключи с TTL (LRU)
# allkeys-lru   — удалять любые ключи (LRU)
# volatile-ttl  — удалять ключи с наименьшим TTL
# noeviction    — не удалять, возвращать ошибку
maxmemory-policy allkeys-lru

# Персистентность
appendonly yes
appendfsync everysec

FAQ

Redis поддерживает персистентность:

RDB (Snapshot): Снимки данных каждые N минут. Быстрый рестарт, но можно потерять последние данные.

AOF (Append Only File): Логирует каждую операцию. Надёжнее, но больше диск и медленнее рестарт.

RDB + AOF: Рекомендуется для продакшена — надёжность AOF + скорость RDB.
Зависит от RAM сервера. Практические рекомендации:

До 25GB — один инстанс Redis
25-100GB — Redis Cluster (шардинг)
>100GB — подумайте, нужен ли вам Redis для этих данных

Redis использует ~1.5-2x от размера данных (overhead на структуры).
Redis:
• Богатые структуры данных (списки, множества, хеши...)
• Персистентность
• Pub/Sub, Streams
• Lua scripting

Memcached:
• Проще (только key-value)
• Многопоточный (лучше использует многоядерные CPU)
• Меньше памяти на элемент

Вывод: В 95% случаев — Redis. Memcached — только для простого кеширования с огромным объёмом данных.
Чтение: Redis Replication — Master + Replicas. Читаем с реплик.

Запись: Redis Cluster — шардинг данных по нескольким мастерам. Автоматическое распределение ключей.

Managed: AWS ElastiCache, Redis Cloud — масштабирование одной кнопкой.
RQ:
• Простой API, быстрый старт
• Только Redis + Python
• Для небольших проектов

Celery:
• Мощный, много возможностей
• Периодические задачи (Beat)
• Цепочки, группы, хорды
• Несколько брокеров

Начните с RQ, переходите на Celery когда понадобятся его возможности.
Redis Streams:
• Проще настройка, уже есть Redis
• До ~100K msg/sec
• Ограничен памятью

Apache Kafka:
• Миллионы msg/sec
• Хранение на диске (терабайты)
• Сложнее настройка и поддержка
• Лучшая экосистема для Big Data

Вывод: Redis Streams для малых-средних нагрузок. Kafka — когда нужны огромные объёмы и гарантии доставки enterprise-уровня.
Встроенные команды:
INFO — статистика сервера
INFO memory — использование памяти
SLOWLOG GET 10 — медленные запросы
CLIENT LIST — подключённые клиенты

Инструменты:
Redis Insight — официальный GUI
Prometheus + Grafana — метрики и дашборды
RedisCommander — веб-интерфейс
🔴 Redis — Итоги
Скорость
<1ms latency, 1M+ ops/sec
📊
Структуры
Strings, Lists, Sets, Hashes, Streams
🎯
Применение
Кеш, сессии, очереди, real-time
📬
Очереди
RQ — просто, Celery — мощно
🌊
Event Streaming
Redis Streams + Consumer Groups
🐍
Python
redis-py, rq, celery[redis]
🚀 Следующие шаги

1. Запустите Redisdocker run -d -p 6379:6379 redis:alpine
2. Попробуйте redis-cli — поиграйте с командами
3. Добавьте кеширование — в свой Python-проект
4. Настройте фоновые задачи — RQ или Celery
5. Изучите Streams — для event-driven архитектуры

Redis — один из самых полезных инструментов. Знание Redis выделяет разработчика и открывает множество архитектурных возможностей.

Cheat Sheet Основные команды Redis
# ═══════════════════════════════════════════════════════════════
#                     REDIS QUICK REFERENCE
# ═══════════════════════════════════════════════════════════════

# ─────────────── STRINGS ───────────────
SET key value                # Установить
GET key                      # Получить
SETEX key 3600 value         # Установить с TTL (секунды)
INCR counter                 # Увеличить на 1
INCRBY counter 10            # Увеличить на N
MSET k1 v1 k2 v2             # Установить несколько
MGET k1 k2 k3                # Получить несколько

# ─────────────── HASHES ───────────────
HSET user:1 name "John"      # Установить поле
HGET user:1 name             # Получить поле
HGETALL user:1               # Все поля
HMSET user:1 a 1 b 2         # Несколько полей
HINCRBY user:1 views 1       # Инкремент поля

# ─────────────── LISTS ───────────────
LPUSH queue task1            # Добавить в начало
RPUSH queue task1            # Добавить в конец
LPOP queue                   # Взять из начала
RPOP queue                   # Взять из конца
BRPOP queue 30               # Блокирующий pop (30 сек)
LRANGE queue 0 -1            # Все элементы
LLEN queue                   # Длина

# ─────────────── SETS ───────────────
SADD tags python redis       # Добавить
SMEMBERS tags                # Все элементы
SISMEMBER tags python        # Проверить наличие
SINTER tags1 tags2           # Пересечение
SUNION tags1 tags2           # Объединение

# ─────────────── SORTED SETS ───────────────
ZADD board 100 player1       # Добавить с score
ZRANGE board 0 9             # Топ 10 (по возрастанию)
ZREVRANGE board 0 9          # Топ 10 (по убыванию)
ZRANK board player1          # Ранг игрока
ZINCRBY board 10 player1     # Увеличить score

# ─────────────── STREAMS ───────────────
XADD events * k1 v1          # Добавить событие
XREAD STREAMS events 0       # Читать с начала
XRANGE events - +            # Все события
XLEN events                  # Количество
XGROUP CREATE events grp 0   # Создать группу
XREADGROUP GROUP grp c1 ...  # Читать в группе
XACK events grp id           # Подтвердить

# ─────────────── KEYS & EXPIRY ───────────────
KEYS user:*                  # Поиск (НЕ для prod!)
SCAN 0 MATCH user:* COUNT 100# Безопасный поиск
DEL key                      # Удалить
EXISTS key                   # Проверить существование
EXPIRE key 3600              # Установить TTL
TTL key                      # Узнать TTL
PERSIST key                  # Убрать TTL

# ─────────────── PUB/SUB ───────────────
SUBSCRIBE channel            # Подписаться
PUBLISH channel message      # Опубликовать
PSUBSCRIBE news:*            # Подписка по паттерну

# ─────────────── ADMIN ───────────────
INFO                         # Статистика сервера
INFO memory                  # Память
DBSIZE                       # Количество ключей
FLUSHDB                      # Очистить текущую БД
SLOWLOG GET 10               # Медленные запросы
CLIENT LIST                  # Подключения

Docker Compose для разработки

YAML docker-compose.yml
version: '3.8'

services:
  # Redis Server
  redis:
    image: redis:7-alpine
    container_name: redis
    ports:
      - "6379:6379"
    volumes:
      - redis-data:/data
    command: redis-server --appendonly yes --maxmemory 256mb --maxmemory-policy allkeys-lru
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 3

  # Redis Insight — GUI для Redis
  redis-insight:
    image: redislabs/redisinsight:latest
    container_name: redis-insight
    ports:
      - "8001:8001"
    depends_on:
      - redis

  # Ваше приложение
  app:
    build: .
    ports:
      - "8000:8000"
    environment:
      - REDIS_URL=redis://redis:6379/0
    depends_on:
      - redis

  # Celery Worker
  celery-worker:
    build: .
    command: celery -A celery_app worker --loglevel=info
    environment:
      - REDIS_URL=redis://redis:6379/0
    depends_on:
      - redis

  # Celery Beat (периодические задачи)
  celery-beat:
    build: .
    command: celery -A celery_app beat --loglevel=info
    environment:
      - REDIS_URL=redis://redis:6379/0
    depends_on:
      - redis

  # Flower — мониторинг Celery
  flower:
    build: .
    command: celery -A celery_app flower --port=5555
    ports:
      - "5555:5555"
    environment:
      - REDIS_URL=redis://redis:6379/0
    depends_on:
      - redis
      - celery-worker

volumes:
  redis-data:
Terminal — Запуск
# Запустить всё
$ docker compose up -d
# Открыть:
• Redis Insight: http://localhost:8001 • Flower: http://localhost:5555 • Your App: http://localhost:8000
# Подключиться к Redis CLI
$ docker exec -it redis redis-cli

© 2025 • Redis Guide для Python-разработчиков

Redis 7.x • Python 3.11+ • Celery 5.x • RQ 1.x