435 lines
15 KiB
Markdown
435 lines
15 KiB
Markdown
# Схема данных Redis в Discours.io
|
||
|
||
## Обзор
|
||
|
||
Redis используется как основное хранилище для кэширования, сессий, токенов и временных данных. Все ключи следуют структурированным паттернам для обеспечения консистентности и производительности.
|
||
|
||
## Принципы именования ключей
|
||
|
||
### Общие правила
|
||
- Использование двоеточия `:` как разделителя иерархии
|
||
- Формат: `{category}:{type}:{identifier}` или `{entity}:{property}:{value}`
|
||
- Константное время поиска через точные ключи
|
||
- TTL для всех временных данных
|
||
|
||
### Категории данных
|
||
1. **Аутентификация**: `session:*`, `oauth_*`, `env_vars:*`
|
||
2. **Кэш сущностей**: `author:*`, `topic:*`, `shout:*`
|
||
3. **Поиск**: `search_cache:*`
|
||
4. **Просмотры**: `migrated_views_*`, `viewed_*`
|
||
5. **Уведомления**: publish/subscribe каналы
|
||
|
||
## 1. Система аутентификации
|
||
|
||
### 1.1 Сессии пользователей
|
||
|
||
#### Структура ключей
|
||
```
|
||
session:{user_id}:{jwt_token} # HASH - данные сессии
|
||
user_sessions:{user_id} # SET - список активных токенов пользователя
|
||
{user_id}-{username}-{token} # STRING - legacy формат (deprecated)
|
||
```
|
||
|
||
#### Данные сессии (HASH)
|
||
```redis
|
||
HGETALL session:123:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...
|
||
```
|
||
**Поля:**
|
||
- `user_id`: ID пользователя (string)
|
||
- `username`: Имя пользователя (string)
|
||
- `token_type`: "session" (string)
|
||
- `created_at`: Unix timestamp создания (string)
|
||
- `last_activity`: Unix timestamp последней активности (string)
|
||
- `auth_data`: JSON строка с данными авторизации (string, optional)
|
||
- `device_info`: JSON строка с информацией об устройстве (string, optional)
|
||
|
||
**TTL**: 30 дней (2592000 секунд)
|
||
|
||
#### Список токенов пользователя (SET)
|
||
```redis
|
||
SMEMBERS user_sessions:123
|
||
```
|
||
**Содержимое**: JWT токены активных сессий пользователя
|
||
**TTL**: 30 дней
|
||
|
||
### 1.2 OAuth токены
|
||
|
||
#### Структура ключей
|
||
```
|
||
oauth_access:{user_id}:{provider} # STRING - access токен
|
||
oauth_refresh:{user_id}:{provider} # STRING - refresh токен
|
||
oauth_state:{state} # HASH - временное состояние OAuth flow
|
||
```
|
||
|
||
#### Access токены
|
||
**Провайдеры**: `google`, `github`, `facebook`, `twitter`, `telegram`, `vk`, `yandex`
|
||
**TTL**: 1 час (3600 секунд)
|
||
**Пример**:
|
||
```redis
|
||
GET oauth_access:123:google
|
||
# Возвращает: access_token_string
|
||
```
|
||
|
||
#### Refresh токены
|
||
**TTL**: 30 дней (2592000 секунд)
|
||
**Пример**:
|
||
```redis
|
||
GET oauth_refresh:123:google
|
||
# Возвращает: refresh_token_string
|
||
```
|
||
|
||
#### OAuth состояние (временное)
|
||
```redis
|
||
HGETALL oauth_state:a1b2c3d4e5f6
|
||
```
|
||
**Поля:**
|
||
- `redirect_uri`: URL для перенаправления после авторизации
|
||
- `csrf_token`: CSRF защита
|
||
- `provider`: Провайдер OAuth
|
||
- `created_at`: Время создания
|
||
|
||
**TTL**: 10 минут (600 секунд)
|
||
|
||
### 1.3 Токены подтверждения
|
||
|
||
#### Структура ключей
|
||
```
|
||
verification:{user_id}:{type}:{token} # HASH - данные токена подтверждения
|
||
```
|
||
|
||
#### Типы подтверждения
|
||
- `email_verification`: Подтверждение email
|
||
- `phone_verification`: Подтверждение телефона
|
||
- `password_reset`: Сброс пароля
|
||
- `email_change`: Смена email
|
||
|
||
**Поля токена**:
|
||
- `user_id`: ID пользователя
|
||
- `token_type`: Тип токена
|
||
- `verification_type`: Тип подтверждения
|
||
- `created_at`: Время создания
|
||
- `data`: JSON с дополнительными данными
|
||
|
||
**TTL**: 1 час (3600 секунд)
|
||
|
||
## 2. Переменные окружения
|
||
|
||
### Структура ключей
|
||
```
|
||
env_vars:{variable_name} # STRING - значение переменной
|
||
```
|
||
|
||
### Примеры переменных
|
||
```redis
|
||
GET env_vars:JWT_SECRET # Секретный ключ JWT
|
||
GET env_vars:REDIS_URL # URL Redis
|
||
GET env_vars:OAUTH_GOOGLE_CLIENT_ID # Google OAuth Client ID
|
||
GET env_vars:FEATURE_REGISTRATION # Флаг функции регистрации
|
||
```
|
||
|
||
**Категории переменных**:
|
||
- **database**: DB_URL, POSTGRES_*
|
||
- **auth**: JWT_SECRET, OAUTH_*
|
||
- **redis**: REDIS_URL, REDIS_HOST, REDIS_PORT
|
||
- **search**: SEARCH_API_KEY, ELASTICSEARCH_URL
|
||
- **integrations**: GOOGLE_ANALYTICS_ID, SENTRY_DSN, SMTP_*
|
||
- **security**: CORS_ORIGINS, ALLOWED_HOSTS
|
||
- **logging**: LOG_LEVEL, DEBUG
|
||
- **features**: FEATURE_*
|
||
|
||
**TTL**: Без ограничения (постоянное хранение)
|
||
|
||
## 3. Кэш сущностей
|
||
|
||
### 3.1 Авторы (пользователи)
|
||
|
||
#### Структура ключей
|
||
```
|
||
author:id:{author_id} # STRING - JSON данные автора
|
||
author:slug:{author_slug} # STRING - ID автора по slug
|
||
author:followers:{author_id} # STRING - JSON массив подписчиков
|
||
author:follows-topics:{author_id} # STRING - JSON массив отслеживаемых тем
|
||
author:follows-authors:{author_id} # STRING - JSON массив отслеживаемых авторов
|
||
author:follows-shouts:{author_id} # STRING - JSON массив отслеживаемых публикаций
|
||
```
|
||
|
||
#### Данные автора (JSON)
|
||
```json
|
||
{
|
||
"id": 123,
|
||
"email": "user@example.com",
|
||
"name": "Имя Пользователя",
|
||
"slug": "username",
|
||
"pic": "https://example.com/avatar.jpg",
|
||
"bio": "Описание автора",
|
||
"email_verified": true,
|
||
"created_at": 1640995200,
|
||
"updated_at": 1640995200,
|
||
"last_seen": 1640995200,
|
||
"stat": {
|
||
"topics": 15,
|
||
"authors": 8,
|
||
"shouts": 42
|
||
}
|
||
}
|
||
```
|
||
|
||
#### Подписчики автора
|
||
```json
|
||
[123, 456, 789] // Массив ID подписчиков
|
||
```
|
||
|
||
#### Подписки автора
|
||
```json
|
||
// author:follows-topics:123
|
||
[1, 5, 10, 15] // ID отслеживаемых тем
|
||
|
||
// author:follows-authors:123
|
||
[45, 67, 89] // ID отслеживаемых авторов
|
||
|
||
// author:follows-shouts:123
|
||
[101, 102, 103] // ID отслеживаемых публикаций
|
||
```
|
||
|
||
**TTL**: Без ограничения (инвалидация при изменениях)
|
||
|
||
### 3.2 Темы
|
||
|
||
#### Структура ключей
|
||
```
|
||
topic:id:{topic_id} # STRING - JSON данные темы
|
||
topic:slug:{topic_slug} # STRING - JSON данные темы
|
||
topic:authors:{topic_id} # STRING - JSON массив авторов темы
|
||
topic:followers:{topic_id} # STRING - JSON массив подписчиков темы
|
||
topic_shouts_{topic_id} # STRING - JSON массив публикаций темы (legacy)
|
||
```
|
||
|
||
#### Данные темы (JSON)
|
||
```json
|
||
{
|
||
"id": 5,
|
||
"title": "Название темы",
|
||
"slug": "tema-slug",
|
||
"description": "Описание темы",
|
||
"pic": "https://example.com/topic.jpg",
|
||
"community": 1,
|
||
"created_at": 1640995200,
|
||
"updated_at": 1640995200,
|
||
"stat": {
|
||
"shouts": 150,
|
||
"authors": 25,
|
||
"followers": 89
|
||
}
|
||
}
|
||
```
|
||
|
||
#### Авторы темы
|
||
```json
|
||
[123, 456, 789] // ID авторов, писавших в теме
|
||
```
|
||
|
||
#### Подписчики темы
|
||
```json
|
||
[111, 222, 333, 444] // ID подписчиков темы
|
||
```
|
||
|
||
**TTL**: Без ограничения (инвалидация при изменениях)
|
||
|
||
### 3.3 Публикации (Shouts)
|
||
|
||
#### Структура ключей
|
||
```
|
||
shouts:{params_hash} # STRING - JSON массив публикаций
|
||
topic_shouts_{topic_id} # STRING - JSON массив публикаций темы
|
||
```
|
||
|
||
#### Примеры ключей публикаций
|
||
```
|
||
shouts:limit=20:offset=0:sort=created_at # Последние публикации
|
||
shouts:author=123:limit=10 # Публикации автора
|
||
shouts:topic=5:featured=true # Рекомендуемые публикации темы
|
||
```
|
||
|
||
**TTL**: 5 минут (300 секунд)
|
||
|
||
## 4. Поисковый кэш
|
||
|
||
### Структура ключей
|
||
```
|
||
search_cache:{normalized_query} # STRING - JSON результаты поиска
|
||
```
|
||
|
||
### Нормализация запроса
|
||
- Приведение к нижнему регистру
|
||
- Удаление лишних пробелов
|
||
- Сортировка параметров
|
||
|
||
### Данные поиска (JSON)
|
||
```json
|
||
{
|
||
"query": "поисковый запрос",
|
||
"results": [
|
||
{
|
||
"type": "shout",
|
||
"id": 123,
|
||
"title": "Заголовок публикации",
|
||
"slug": "publication-slug",
|
||
"score": 0.95
|
||
}
|
||
],
|
||
"total": 15,
|
||
"cached_at": 1640995200
|
||
}
|
||
```
|
||
|
||
**TTL**: 10 минут (600 секунд)
|
||
|
||
## 5. Система просмотров
|
||
|
||
### Структура ключей
|
||
```
|
||
migrated_views_{timestamp} # HASH - просмотры публикаций
|
||
migrated_views_slugs # HASH - маппинг slug -> id
|
||
viewed:{shout_id} # STRING - счетчик просмотров
|
||
```
|
||
|
||
### Мигрированные просмотры (HASH)
|
||
```redis
|
||
HGETALL migrated_views_1640995200
|
||
```
|
||
**Поля**:
|
||
- `{shout_id}`: количество просмотров (string)
|
||
- `_timestamp`: время создания записи
|
||
- `_total`: общее количество записей
|
||
|
||
### Маппинг slug -> ID
|
||
```redis
|
||
HGETALL migrated_views_slugs
|
||
```
|
||
**Поля**: `{shout_slug}` -> `{shout_id}`
|
||
|
||
**TTL**: Без ограничения (данные аналитики)
|
||
|
||
## 6. Pub/Sub каналы
|
||
|
||
### Каналы уведомлений
|
||
```
|
||
notifications:{user_id} # Персональные уведомления
|
||
notifications:global # Глобальные уведомления
|
||
notifications:topic:{topic_id} # Уведомления темы
|
||
notifications:shout:{shout_id} # Уведомления публикации
|
||
```
|
||
|
||
### Структура сообщения (JSON)
|
||
```json
|
||
{
|
||
"type": "notification_type",
|
||
"user_id": 123,
|
||
"entity_type": "shout",
|
||
"entity_id": 456,
|
||
"action": "created|updated|deleted",
|
||
"data": {
|
||
"title": "Заголовок",
|
||
"author": "Автор"
|
||
},
|
||
"timestamp": 1640995200
|
||
}
|
||
```
|
||
|
||
## 7. Временные данные
|
||
|
||
### Ключи блокировок
|
||
```
|
||
lock:{operation}:{entity_id} # STRING - блокировка операции
|
||
```
|
||
|
||
**TTL**: 30 секунд (автоматическое снятие блокировки)
|
||
|
||
### Ключи состояния
|
||
```
|
||
state:{process}:{identifier} # HASH - состояние процесса
|
||
```
|
||
|
||
**TTL**: От 1 минуты до 1 часа в зависимости от процесса
|
||
|
||
## 8. Мониторинг и статистика
|
||
|
||
### Ключи метрик
|
||
```
|
||
metrics:{metric_name}:{period} # STRING - значение метрики
|
||
stats:{entity}:{timeframe} # HASH - статистика сущности
|
||
```
|
||
|
||
### Примеры метрик
|
||
```
|
||
metrics:active_sessions:hourly # Количество активных сессий
|
||
metrics:cache_hits:daily # Попадания в кэш за день
|
||
stats:topics:weekly # Статистика тем за неделю
|
||
```
|
||
|
||
**TTL**: От 1 часа до 30 дней в зависимости от типа метрики
|
||
|
||
## 9. Оптимизация и производительность
|
||
|
||
### Пакетные операции
|
||
Используются Redis pipelines для атомарных операций:
|
||
```python
|
||
# Пример создания сессии
|
||
commands = [
|
||
("hset", (token_key, "user_id", user_id)),
|
||
("hset", (token_key, "created_at", timestamp)),
|
||
("expire", (token_key, ttl)),
|
||
("sadd", (user_tokens_key, token)),
|
||
]
|
||
await redis.execute_pipeline(commands)
|
||
```
|
||
|
||
### Стратегии кэширования
|
||
1. **Write-through**: Немедленное обновление кэша при изменении данных
|
||
2. **Cache-aside**: Lazy loading с обновлением при промахе
|
||
3. **Write-behind**: Отложенная запись в БД
|
||
|
||
### Инвалидация кэша
|
||
- **Точечная**: Удаление конкретных ключей при изменениях
|
||
- **По префиксу**: Массовое удаление связанных ключей
|
||
- **TTL**: Автоматическое истечение для временных данных
|
||
|
||
## 10. Мониторинг
|
||
|
||
### Команды диагностики
|
||
```bash
|
||
# Статистика использования памяти
|
||
redis-cli info memory
|
||
|
||
# Количество ключей по типам
|
||
redis-cli --scan --pattern "session:*" | wc -l
|
||
redis-cli --scan --pattern "author:*" | wc -l
|
||
redis-cli --scan --pattern "topic:*" | wc -l
|
||
|
||
# Размер конкретного ключа
|
||
redis-cli memory usage session:123:token...
|
||
|
||
# Анализ истечения ключей
|
||
redis-cli --scan --pattern "*" | xargs -I {} redis-cli ttl {}
|
||
```
|
||
|
||
### Проблемы и решения
|
||
1. **Память**: Использование TTL для временных данных
|
||
2. **Производительность**: Pipeline операции, connection pooling
|
||
3. **Консистентность**: Транзакции для критических операций
|
||
4. **Масштабирование**: Шардирование по user_id для сессий
|
||
|
||
## 11. Безопасность
|
||
|
||
### Принципы
|
||
- TTL для всех временных данных предотвращает накопление мусора
|
||
- Раздельное хранение секретных данных (токены) и публичных (кэш)
|
||
- Использование pipeline для атомарных операций
|
||
- Регулярная очистка истекших ключей
|
||
|
||
### Рекомендации
|
||
- Мониторинг использования памяти Redis
|
||
- Backup критичных данных (переменные окружения)
|
||
- Ограничение размера значений для предотвращения OOM
|
||
- Использование отдельных баз данных для разных типов данных
|