Что такое Redis, основные сценарии использования, установка и запуск
Redis — это не просто кэш. Это мощный инструмент для решения десятков production-задач: от кэширования API до очередей задач, real-time аналитики и управления сессиями.
Redis (Remote Dictionary Server) — это in-memory база данных типа key-value store с открытым исходным кодом. Ключевые особенности:
| Задача | Как Redis помогает |
|---|---|
| Кэширование | Кэш ответов API, результатов тяжёлых запросов, HTML-страниц |
| Сессии | Хранение пользовательских сессий с TTL для авто-очистки |
| Rate limiting | Ограничение запросов на IP/пользователя через INCR + EXPIRE |
| Очереди задач | Lists или Streams для фоновой обработки через Celery/ARQ |
| Pub/Sub | Real-time уведомления, чаты, веб-сокеты |
| Лидерборды | Sorted Sets для рейтингов с автоматической сортировкой |
| Аналитика | HyperLogLog для уникальных посетителей, Bitmaps для активности |
| Геоданные | Geo-команды для поиска объектов рядом |
| Распределённые блокировки | Координация между воркерами через SETNX |
| Задача | Почему не Redis |
|---|---|
| Основная БД | Нет ACID-гарантий, ограниченный объём RAM, нет сложных запросов |
| Долгосрочное хранение | Дорогая память, риск потери данных при сбое питания |
| Сложные JOIN | Нет реляционной модели, только простые lookup по ключу |
| Большие бинарные файлы | Максимальный размер значения 512 MB, но это плохая практика |
┌─────────────────────────────────────────────────────────┐
│ Redis Server │
│ ┌───────────────────────────────────────────────────┐ │
│ │ In-Memory Database │ │
│ │ ┌─────┐ ┌──────┐ ┌─────┐ ┌─────┐ ┌───────────┐ │ │
│ │ │Strings│ │Hashes│ │Lists│ │Sets │ │Sorted Sets│ │ │
│ │ └─────┘ └──────┘ └─────┘ └─────┘ └───────────┘ │ │
│ │ ┌────────┐ ┌───────────┐ ┌───────┐ ┌─────────┐ │ │
│ │ │Bitmaps│ │HyperLogLog│ │Streams│ │ Geo │ │ │
│ │ └────────┘ └───────────┘ └───────┘ └─────────┘ │ │
│ └───────────────────────────────────────────────────┘ │
│ │ │
│ ┌────────────────┼────────────────┐ │
│ │ │ │ │
│ ┌────▼────┐ ┌─────▼─────┐ ┌──────▼──────┐ │
│ │ RDB │ │ AOF │ │ Replication │ │
│ │ (snapshot)│ │ (logging) │ │ (async) │ │
│ └──────────┘ └────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────┘
Однопоточная модель: Redis обрабатывает команды в одном потоке, что исключает race conditions и упрощает логику. I/O операции выполняются через event loop (epoll/kqueue).
Почему быстро:
Создайте docker-compose.yml:
version: '3.8'
services:
redis:
image: redis:7-alpine
container_name: redis_dev
ports:
- "6379:6379"
volumes:
- redis_data:/data
command: redis-server --appendonly yes
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
volumes:
redis_data:Запуск:
docker-compose up -d redisФлаги:
--appendonly yes — включает AOF персистентность--requirepass <password> — устанавливает пароль (для production!)brew install redis
brew services start redissudo apt update
sudo apt install redis-server
sudo systemctl start redis-server
sudo systemctl enable redis-serverredis-cli ping
# PONGRedis поставляется с CLI-клиентом redis-cli. Основные команды:
# Проверка существования
EXISTS user:1001
# Удаление
DEL user:1001
# Время жизни (TTL) в секундах
TTL session:abc123
# Установить TTL
EXPIRE session:abc123 3600
# Найти ключи по паттерну (не использовать в production!)
KEYS user:*
# Итерация по ключам (безопасно для production)
SCAN 0 MATCH user:* COUNT 100
# Тип значения
TYPE user:1001⚠️ Никогда не используйте
KEYSв production — он блокирует Redis до завершения. ИспользуйтеSCAN.
# Установка значения
SET user:1001:name "Alice"
# Получение значения
GET user:1001:name
# Установка с TTL (атомарно)
SETEX session:abc123 3600 '{"user_id": 1001}'
# Инкремент (атомарно)
INCR page:views:123
INCRBY page:views:123 5
# Декремент
DECR counter:visits# Установка полей
HSET user:1001 name "Alice" age 25 email "alice@example.com"
# Получение поля
HGET user:1001 name
# Получение всех полей
HGETALL user:1001
# Удаление поля
HDEL user:1001 age
# Инкремент поля
HINCRBY user:1001 login_count 1# Push слева (в начало)
LPUSH tasks "task1" "task2" "task3"
# Push справа (в конец)
RPUSH queue:emails "email1@example.com"
# Pop слева
LPOP tasks
# Pop справа
RPOP queue:emails
# Длина списка
LLEN tasks
# Диапазон элементов
LRANGE tasks 0 -1Установите библиотеку redis-py:
pip install redisБазовое подключение:
import redis
# Синхронный клиент
client = redis.Redis(
host='localhost',
port=6379,
db=0,
decode_responses=True, # Возвращать str вместо bytes
socket_timeout=5, # Таймаут операций
socket_connect_timeout=5, # Таймаут подключения
)
# Проверка подключения
client.ping() # True
# Базовые операции
client.set('user:1001:name', 'Alice')
client.get('user:1001:name') # 'Alice'import redis.asyncio as redis
# Асинхронный клиент
redis_client = redis.Redis(
host='localhost',
port=6379,
db=0,
decode_responses=True,
socket_timeout=5,
socket_connect_timeout=5,
)
# Использование в async контексте
await redis_client.set('user:1001:name', 'Alice')
value = await redis_client.get('user:1001:name')| Аспект | Синхронный (redis.Redis) | Асинхронный (redis.asyncio.Redis) |
|---|---|---|
| Блокировка | Блокирует поток до завершения операции | Не блокирует, освобождает event loop |
| Где использовать | Синхронный код, скрипты, CLI | FastAPI, asyncio-приложения |
| Вызов методов | client.get(), client.set() | await client.get(), await client.set() |
| Производительность | Один запрос = один поток | Множество запросов в одном потоке |
| Подключение | Создаётся сразу при инициализации | Создаётся при первом await (лениво) |
# ❌ ПЛОХО: блокирует event loop
from fastapi import FastAPI
import redis
app = FastAPI()
client = redis.Redis(host='localhost', port=6379)
@app.get("/user/{user_id}")
def get_user(user_id: int):
# Блокирует весь сервер на время запроса к Redis!
data = client.get(f"user:{user_id}")
return {"data": data}# ✅ ХОРОШО: не блокирует event loop
from fastapi import FastAPI
import redis.asyncio as redis
app = FastAPI()
redis_client = redis.Redis(host='localhost', port=6379)
@app.get("/user/{user_id}")
async def get_user(user_id: int):
# Освобождает event loop во время ожидания
data = await redis_client.get(f"user:{user_id}")
return {"data": data}from contextlib import asynccontextmanager
import redis.asyncio as redis
@asynccontextmanager
async def lifespan(app: FastAPI):
# При запуске приложения
redis_client = redis.Redis(
host='localhost',
port=6379,
decode_responses=True,
)
await redis_client.ping() # Проверка подключения
yield {"redis": redis_client}
# При остановке приложения
await redis_client.close()
app = FastAPI(lifespan=lifespan)
@app.get("/user/{user_id}")
async def get_user(user_id: int, redis: redis.Redis = Depends()):
data = await redis.get(f"user:{user_id}")
return {"data": data}Правило: для FastAPI всегда используйте
redis.asyncio— синхронный клиент заблокирует event loop и убьёт производительность.
| Настройка | Development | Production |
|---|---|---|
| Пароль | Не требуется | requirepass <strong-password> |
| Bind | 0.0.0.0 | 127.0.0.1 или private IP |
| Port | 6379 | 6379 (или кастомный) |
| Персистентность (сохранение на диск) | Выключена или AOF | RDB + AOF |
| Maxmemory | Не ограничено | 70-80% RAM сервера |
| Eviction policy | noeviction | allkeys-lru или volatile-lru |
| TLS | Нет | Да (для внешних подключений) |
| Rename dangerous commands | Нет | Да (FLUSHALL, CONFIG, DEBUG) |
Пример production redis.conf:
# Безопасность
bind 127.0.0.1
port 6379
requirepass SuperSecretPassword123!
# Переименование опасных команд
rename-command FLUSHALL ""
rename-command FLUSHDB ""
rename-command CONFIG ""
rename-command DEBUG ""
# Персистентность
save 900 1
save 300 10
save 60 10000
appendonly yes
appendfsync everysec
# Память
maxmemory 4gb
maxmemory-policy allkeys-lru
# Логирование
loglevel notice
logfile /var/log/redis/redis-server.logВ следующей теме изучим все структуры данных Redis с примерами использования в реальных проектах.
Проверьте понимание → ответьте на вопросы в intro.json
Вопросы ещё не добавлены
Вопросы для этой подтемы ещё не добавлены.