Данные и хранилища
~25 мин

NoSQL базы данных

MongoDB, Redis, Cassandra — когда что использовать, CAP-теорема.

NoSQL базы — Redis, MongoDB и когда что использовать

Загрузка интерактивного виджета...

Загрузка интерактивного виджета...

PostgreSQL закрывает 90% задач. Но есть ситуации, где реляционная база — не лучший выбор. Нужен кеш предсказаний с ответом за миллисекунду? Redis. Хранить логи экспериментов с произвольными полями? MongoDB. Писать миллионы событий в секунду? Cassandra. Каждая NoSQL-база заточена под конкретный паттерн, где SQL-база буксует.

Redis — кеш, feature store, очереди

Загрузка интерактивного виджета...

Redis хранит всё в оперативной памяти, поэтому отвечает за микросекунды. В ML-проектах Redis используется постоянно: кеш предсказаний (не гонять модель дважды для одних и тех же фичей), online feature store (подавать фичи на inference за < 10 мс), очереди задач (Celery backend), rate limiting для API.

import redis
import json
import hashlib

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

# === Кеш предсказаний модели ===
def predict_with_cache(features: dict) -> float:
    """Если уже считали предсказание для таких фичей — берём из кеша."""
    key = f"pred:{hashlib.md5(json.dumps(features, sort_keys=True).encode()).hexdigest()}"

    cached = r.get(key)
    if cached is not None:
        return float(cached)  # hit! Ответ за 0.1 мс вместо 50 мс

    prediction = model.predict([list(features.values())])[0]
    r.setex(key, 3600, str(prediction))  # кешируем на 1 час
    return prediction

# === Online Feature Store (простейший вариант) ===
# Записываем фичи пользователя
r.hset("user:12345", mapping={
    "total_orders_30d": 15,
    "avg_order_value": 2340.50,
    "days_since_last_order": 3,
    "segment": "premium"
})

# Читаем фичи для inference — за < 1 мс
features = r.hgetall("user:12345")

# === Очередь задач ===
r.lpush("inference:queue", json.dumps({
    "request_id": "req-789",
    "features": [1.2, 3.4, 5.6]
}))

💡 Redis в продакшне

Redis — это in-memory, значит при перезагрузке данные могут потеряться. Для продакшна включи RDB-снепшоты или AOF-логирование. А ещё лучше — используй Redis Cluster для отказоустойчивости. Но для кеша потеря данных не критична — кеш просто прогреется заново.

Redis на практике — не только кеш

Redis часто воспринимают как 'просто кеш', но это полноценный сервер структур данных. Вот реальные кейсы, которые ML-инженер использует каждый день.

  • Кеш инференса — одинаковый запрос → одинаковый ответ. SET prediction:{hash} result EX 3600. Экономит GPU-время.
  • Feature Store Online — HSET user:123 age 25 city moscow. Все фичи юзера в одном HASH, latency <1ms для serving.
  • Очередь ML-задач — RPUSH inference_queue '{"user_id": 123}', воркер делает LPOP. Простая и надёжная.
  • Распределённые блокировки — SET training_lock worker_1 NX EX 3600. Два воркера не начнут обучение одновременно.
  • Rate Limiting — INCR api:user:123:minute, EXPIRE 60. Не больше 100 запросов к API в минуту.
  • Pub/Sub — PUBLISH model_updates 'v2.1 deployed'. Все сервисы мгновенно узнают о новой версии модели.
import redis
r = redis.Redis()

# Кеш инференса
r.set(f'pred:{query_hash}', result, ex=3600)
cached = r.get(f'pred:{query_hash}')

# Feature Store
r.hset('user:123', mapping={'age': 25, 'city': 'moscow', 'ltv': 150.5})
features = r.hgetall('user:123')

# Очередь задач
r.rpush('inference_queue', json.dumps(task))
task = r.lpop('inference_queue')

MongoDB — когда схема непредсказуема

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

from pymongo import MongoClient
from datetime import datetime

client = MongoClient("mongodb://localhost:27017")
db = client["ml_platform"]

# === Логи экспериментов (у каждого свои параметры) ===
db.experiments.insert_one({
    "model_name": "catboost_v3",
    "params": {"learning_rate": 0.05, "depth": 8, "iterations": 1000},
    "metrics": {"auc": 0.934, "logloss": 0.287},
    "features_used": ["age", "income", "clicks_7d"],
    "dataset_size": ,
    "created_at": datetime.utcnow(),
    "tags": ["production-ready"],
})

# Найти лучшие эксперименты
best = db.experiments.find(
    {"metrics.auc": {"$gt": 0.9}},
).sort("metrics.auc", -1).limit(5)

# === Метаданные моделей ===
db.models.insert_one({
    "name": "churn-predictor",
    "version": "2.3",
    "artifact_path": "s3://models/churn/v2.3/model.pkl",
    "input_schema": {"age": "int", "revenue_30d": "float"},
    "deployed_at": datetime.utcnow(),
    "a_b_test": "experiment-42",
})

Cassandra — для миллиардов строк

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

-- Cassandra: таблица спроектирована ПОД КОНКРЕТНЫЙ ЗАПРОС
-- "Дай мне все события пользователя за конкретный день"
CREATE TABLE events_by_user (
    user_id UUID,
    event_date DATE,
    event_time TIMESTAMP,
    event_type TEXT,
    properties MAP<TEXT, TEXT>,
    PRIMARY KEY ((user_id, event_date), event_time)
) WITH CLUSTERING ORDER BY (event_time DESC);

-- Быстрый запрос (по partition key) — миллисекунды
SELECT * FROM events_by_user
WHERE user_id = ? AND event_date = '2024-03-15';

-- А вот так НЕЛЬЗЯ — полный скан всего кластера:
-- SELECT * FROM events_by_user WHERE event_type = 'purchase';

⚠️ Cassandra — не замена PostgreSQL

Cassandra не умеет JOIN, произвольные WHERE, подзапросы. Это совсем другой подход: query-first design. Сначала определяешь запросы → потом создаёшь таблицы под них. Одни и те же данные могут лежать в нескольких таблицах — это нормально для Cassandra.

Когда что использовать — шпаргалка

  • PostgreSQL — 90% задач. ACID, JOIN-ы, аналитика. Если не уверен — бери PostgreSQL
  • Redis — кеш предсказаний, online features, сессии, очереди. Ответ за < 1 мс
  • MongoDB — гибкая схема: метаданные моделей, логи экспериментов, конфиги. Быстрый прототип
  • Cassandra — миллиарды строк, высокая скорость записи, IoT/time-series. Нужна продуманная модель данных
  • ClickHouse — аналитика на терабайтах в реальном времени. Запросы вида "посчитай агрегат по миллиарду строк"

На практике в ML-проектах чаще всего встречается связка: PostgreSQL для основных данных + Redis для кеша/online features + S3/BigQuery для аналитики. MongoDB — если команда уже её использует или нужна гибкая схема для метаданных. Cassandra — в больших компаниях с тяжёлой телеметрией.

🎯 На собесе

Когда используете NoSQL вместо PostgreSQL? Redis — когда нужна латентность < 1 мс (кеш, real-time features). MongoDB — когда схема данных непредсказуема (разные эксперименты с разными параметрами). Cassandra — когда объём записи огромный и данные распределены по кластеру. Главное — обосновать выбор под конкретную задачу, а не "NoSQL модно".