Skip to main content
backend30 ноября 2025 г.11 мин чтения

Выбор правильной базы данных: реляционные против NoSQL для реальных проектов

Практическая система принятия решений для выбора между PostgreSQL, MongoDB, Firebase, Redis и другими базами данных на основе ваших реальных требований.

databasepostgresqlmongodb
Выбор правильной базы данных: реляционные против NoSQL для реальных проектов

"Это зависит от обстоятельств" — честный, но бесполезный ответ на любой вопрос о базах данных. Когда вы начинаете проект и вам нужно выбрать базу данных, вам нужно нечто более конкретное, чем список компромиссов. Вам нужна система принятия решений, которая учитывает ваши реальные требования — форму данных, шаблоны запросов, опыт команды, ожидания по масштабированию и операционную сложность.

Я использовал PostgreSQL, MongoDB, Firebase/Firestore, Redis и SQLite в различных производственных проектах. Каждый выбор был правильным для своего контекста, а некоторые были ошибочными и требовали миграции. Шаблоны, лежащие в основе этих решений, более полезны, чем любое сравнительное тестирование.

Система принятия решений

Прежде чем рассматривать отдельные базы данных, ответьте на эти пять вопросов о вашем проекте:

1. Какова форма ваших данных?

Речь не идет о "реляционных против документных". Речь идет о том, как ваши сущности данных связаны друг с другом.

Высокореляционные: У пользователей есть заказы, у заказов есть товары, товары принадлежат категориям, категории имеют иерархии. Если вы нарисуете свою модель данных, и она будет выглядеть как граф со множеством связей, вам нужны сильные реляционные возможности.

Документоориентированные: Каждая сущность относительно самодостаточна. Запись в блоге содержит заголовок, текст, теги и информацию об авторе. Вам редко требуется объединять данные разных типов сущностей.

Ключ-значение: Вам нужно хранить и извлекать данные по ключу. Данные сессий, конфигурация, флаги функций.

Временные ряды: События, метрики, логи. Интенсивная запись, только добавление, запросы по временному диапазону.

2. Каковы ваши шаблоны запросов?

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

Ad-hoc запросы: Пользователи могут искать, фильтровать и агрегировать данные непредсказуемыми способами. Аналитические панели, функции поиска, инструменты отчетности. Они отдают предпочтение гибким механизмам запросов.

Простые поиски: Большинство операций чтения — это "получить документ по ID". Документные базы данных и хранилища ключ-значение превосходны в этом.

3. Каковы ваши требования к согласованности?

Строгая согласованность: Банковское дело, инвентаризация, все, где чтение устаревших данных вызывает реальные проблемы. Реляционные базы данных с ACID-транзакциями — безопасный выбор.

Конечная согласованность: Социальные ленты, аналитика, кэширование. Вы можете допустить чтение немного устаревших данных в обмен на производительность и доступность.

4. Какова ваша траектория масштабирования?

Вертикальное масштабирование подходит: Большинство приложений. Если ваша база данных помещается на одной машине с возможностью роста, любая база данных подойдет. PostgreSQL на современном сервере обрабатывает миллионы строк без особых усилий.

Требуется горизонтальное масштабирование: Вам необходимо распределить данные по нескольким машинам. Это встречается реже, чем думает большинство разработчиков, но когда это необходимо, ваш выбор базы данных ограничен.

5. Что знает ваша команда?

Этот фактор недооценивается. Команда экспертов по PostgreSQL будет более продуктивной с PostgreSQL, даже если MongoDB теоретически лучше подходит для модели данных. Стоимость изучения новой базы данных — отладка незнакомых ошибок, изучение лучших операционных практик, понимание характеристик производительности — реальна и значительна.

PostgreSQL: Выбор по умолчанию

Если вы не уверены, выберите PostgreSQL. Это не спорное мнение среди бэкенд-инженеров — это консенсус. PostgreSQL обрабатывает реляционные данные, JSON-документы, полнотекстовый поиск, геопространственные запросы и данные временных рядов. Он не является лучшим ни в одном из этих направлений, но достаточно хорош во всех, чтобы служить единой базой данных для большинства приложений.

Преимущества

ACID-транзакции. Когда вам нужно атомарно обновить несколько таблиц — списание запасов и создание заказа, перевод денег между счетами — PostgreSQL гарантирует корректность.

Богатые возможности запросов. Оконные функции, CTE (Common Table Expressions), рекурсивные запросы, латеральные соединения. SQL — это не просто SELECT * FROM — это мощный язык запросов, который может выражать сложную аналитику без перемещения данных на уровень приложения.

-- Find each user's most recent order with running total
WITH ranked_orders AS (
  SELECT
    user_id,
    order_id,
    total,
    created_at,
    ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC) as rn,
    SUM(total) OVER (PARTITION BY user_id ORDER BY created_at) as running_total
  FROM orders
)
SELECT * FROM ranked_orders WHERE rn = 1;

Столбцы JSONB. Когда часть ваших данных действительно не имеет схемы — пользовательские предпочтения, динамические ответы форм, полезные данные сторонних вебхуков — вы можете хранить их как JSONB и запрашивать с полной поддержкой индексирования.

-- Store and query semi-structured data
CREATE TABLE products (
  id SERIAL PRIMARY KEY,
  name TEXT NOT NULL,
  attributes JSONB DEFAULT '{}'
);

-- Index a specific JSON path
CREATE INDEX idx_products_color ON products USING GIN ((attributes->'color'));

-- Query JSON data
SELECT name FROM products
WHERE attributes->>'color' = 'blue'
AND (attributes->>'weight')::numeric < 10;

Экосистема расширений. PostGIS для геопространственных данных, pg_trgm для нечеткого текстового поиска, TimescaleDB для временных рядов, pgvector для AI-встраиваний. Модель расширений означает, что PostgreSQL может адаптироваться к специализированным нагрузкам без замены вашей основной базы данных.

Когда PostgreSQL — не лучший выбор

  • Массовая пропускная способность записи с горизонтальным масштабированием. PostgreSQL хорошо масштабируется вертикально (более мощная машина), но горизонтальное шардирование сложно. Если вам нужно записывать миллионы событий в секунду на десятки машин, рассмотрите специализированные решения.
  • Быстрое прототипирование, когда изменения схемы происходят ежедневно. На самых ранних этапах стартапа, когда модель данных кардинально меняется каждую неделю, накладные расходы на миграции могут замедлить вас по сравнению с бессхемными вариантами.
  • Когда у вашей команды нет опыта работы с SQL, а сроки проекта не позволяют его изучить. Это редкость, но случается.

MongoDB: Когда документы имеют смысл

MongoDB получает больше критики, чем заслуживает. Ранний маркетинг ("без схемы! веб-масштаб!") создал нереалистичные ожидания, и многие разработчики использовали его для случаев, когда PostgreSQL был бы лучше. Но есть реальные сценарии, когда MongoDB — правильный выбор.

Когда MongoDB выигрывает

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

Event sourcing и логирование. Высокая пропускная способность записи с документами, которые в основном записываются и читаются, редко обновляются и никогда не объединяются.

Встроенные документы уменьшают количество соединений. Если заказу всегда нужны его товары, и товары никогда не доступны без своего заказа, встраивание товаров внутрь документа заказа означает, что ваш самый распространенный запрос — это поиск одного документа вместо соединения двух таблиц.

// MongoDB document with embedded sub-documents
{
  _id: ObjectId("..."),
  customer: {
    name: "John Doe",
    email: "john@example.com"
  },
  items: [
    { product: "Widget", quantity: 2, price: 9.99 },
    { product: "Gadget", quantity: 1, price: 24.99 }
  ],
  total: 44.97,
  status: "shipped",
  createdAt: ISODate("2026-03-01T10:00:00Z")
}

Горизонтальное масштабирование — это первоклассная функция. Шардирование MongoDB встроено и хорошо документировано. Если вам действительно нужно распределять данные по множеству узлов (сотни миллионов документов с высокой пропускной способностью), MongoDB справляется с этим более изящно, чем PostgreSQL.

Когда MongoDB — неправильный выбор

  • Высокореляционные данные. Если вы пишете агрегации $lookup (версия соединений в MongoDB) в каждом запросе, вы выбрали не ту базу данных.
  • Когда вам часто нужны транзакции между коллекциями. MongoDB поддерживает транзакции с несколькими документами, но они добавляют задержку и сложность. Если ваши основные операции требуют атомарности между коллекциями, модель транзакций PostgreSQL более естественна.
  • Когда вам действительно нужна схема. "Бессхемность" звучит освобождающе, пока вы не осознаете, что каждый фрагмент кода, который читает из базы данных, неявно является схемой. Без схемы, принудительно применяемой базой данных, вы переносите валидацию на уровень приложения, где легче допустить ошибки и сложнее обеспечить согласованность.

Firebase и Firestore: Быстрая разработка

Firebase Realtime Database и Firestore обслуживают определенную нишу: приложения, где скорость разработки важнее чистоты моделирования данных, и где бэкенд является в первую очередь уровнем сохранения и синхронизации данных, а не сложным движком бизнес-логики.

Когда Firebase сияет

Мобильные приложения с синхронизацией в реальном времени. SDK Firebase по умолчанию обрабатывают автономное кэширование, слушатели в реальном времени и разрешение конфликтов. Создание этого с нуля с помощью PostgreSQL требует уровня WebSocket, стратегии кэширования и значительного количества пользовательского кода.

Небольшие команды без бэкенд-инженеров. Firebase устраняет необходимость управлять сервером базы данных, создавать уровень API, реализовывать аутентификацию и обрабатывать хранение файлов. Для команды из двух человек, создающей MVP, это снижение операционных накладных расходов может стать решающим фактором между выпуском продукта и его невыпуском.

Прототипирование и валидация. Когда вам нужно протестировать идею с реальными пользователями за две недели, Firebase позволяет полностью сосредоточиться на клиентском приложении. Если идея подтвердится, вы сможете позже мигрировать на более традиционный бэкенд.

Ограничения Firebase

Ограничения запросов в Firestore. Вы не можете выполнять запросы по полям, которые не проиндексированы. Вы не можете использовать фильтры неравенства по нескольким полям. Вы не можете выполнять полнотекстовый поиск. Эти ограничения вынуждают вас агрессивно денормализовать и иногда дублировать данные между коллекциями.

// Firestore: You CAN'T do this
db.collection('products')
  .where('price', '>', 10)
  .where('rating', '>', 4)
  .orderBy('name')  // Error: needs composite index on price + rating + name

// Firestore: You CAN do this (with a composite index)
db.collection('products')
  .where('price', '>', 10)
  .where('rating', '>', 4)
  .orderBy('price')  // Must order by a field used in inequality

Привязка к поставщику. Ваша модель данных, правила безопасности и шаблоны запросов — все это специфично для Firebase. Миграция с Firebase — это переписывание, а не миграция.

Непредсказуемость стоимости. Firebase выставляет счета за каждое чтение/запись документа. Плохо оптимизированный запрос или слушатель в реальном времени для большой коллекции может привести к неожиданным счетам. Я видел проекты, где один неправильно настроенный слушатель стоил больше, чем выделенный сервер PostgreSQL за год.

Ограниченная серверная логика. Cloud Functions могут обрабатывать некоторую серверную обработку, но сложная бизнес-логика — многошаговые транзакции, агрегация данных, фоновая обработка — в любом случае требует либо творческих обходных путей, либо отдельного бэкенда.

Решение: Firebase против традиционного бэкенда

Фактор Firebase PostgreSQL + API
Время до MVP Дни Недели
Операционные накладные расходы Почти нулевые Умеренные
Гибкость запросов Ограниченная Полный SQL
Стоимость при масштабировании Непредсказуемая Предсказуемая
Привязка к поставщику Высокая Низкая
Поддержка офлайн Встроена Нужно создавать
Сложная бизнес-логика Сложно Естественно
Требуемая команда Только фронтенд Фронтенд + Бэкенд

Redis: Кэширование, очереди и многое другое

Redis не является основной базой данных для большинства приложений (хотя Redis с модулями персистентности может выполнять эту роль). Это высокопроизводительное хранилище структур данных, которое превосходно справляется с конкретными задачами.

Кэширование

Наиболее распространенный вариант использования Redis. Кэширование дорогостоящих запросов к базе данных, ответов API или вычисленных результатов.

import redis
import json

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

def get_user_profile(user_id: str):
    # Check cache first
    cached = r.get(f"user:{user_id}")
    if cached:
        return json.loads(cached)

    # Cache miss — query database
    profile = db.query("SELECT * FROM users WHERE id = %s", user_id)

    # Cache for 5 minutes
    r.setex(f"user:{user_id}", 300, json.dumps(profile))
    return profile

Хранение сессий

Модель ключ-значение Redis с поддержкой TTL (time-to-live) естественным образом подходит для данных сессий. Она быстрее, чем сессии, поддерживаемые базой данных, и проще, чем решения на основе JWT для stateful-приложений.

Ограничение скорости запросов

def is_rate_limited(user_id: str, limit: int = 100, window: int = 60) -> bool:
    key = f"rate:{user_id}"
    current = r.incr(key)
    if current == 1:
        r.expire(key, window)
    return current > limit

Очереди задач

Списки и потоки Redis отлично подходят для очередей задач. Библиотеки, такие как Bull (Node.js), Celery (Python) и Sidekiq (Ruby), используют Redis в качестве своего брокера сообщений.

Когда не следует использовать Redis

  • В качестве единственной базы данных. Redis по умолчанию находится в оперативной памяти. Даже с персистентностью (снимки RDB или логи AOF) он не предназначен для данных, которые вы не можете позволить себе потерять.
  • Для сложных запросов. Структуры данных Redis (строки, списки, множества, отсортированные множества, хеши) мощны, но не могут быть запрошены как база данных. Вы получаете доступ к данным по ключу, а не по произвольным условиям.
  • Когда у вас нет проблемы с кэшированием. Добавление Redis в стек, который не нуждается в кэшировании, добавляет операционную сложность без какой-либо пользы. Запрос PostgreSQL, который занимает 5 мс, не нуждается в кэшировании.

Использование нескольких баз данных

Большинство производственных приложений умеренной сложности используют более одной базы данных. Главное — использовать каждую для того, в чем она лучшая, а не пытаться заставить одну базу данных обрабатывать все.

Распространенный шаблон для веб-приложения:

  • PostgreSQL для основных бизнес-данных (пользователи, заказы, товары).
  • Redis для кэширования, сессий и ограничения скорости запросов.
  • S3 (или эквивалент) для хранения файлов (изображения, документы, резервные копии).

Для приложения реального времени:

  • PostgreSQL для постоянных данных и сложных запросов.
  • Redis для pub/sub и кэширования.
  • Firebase/Supabase для синхронизации в реальном времени с мобильными клиентами.

Шаблон интеграции

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

PostgreSQL (source of truth)
    ├── Redis (read cache, expires after TTL)
    ├── Elasticsearch (search index, synced via change data capture)
    └── Firebase (mobile sync, updated via webhooks)

Никогда не допускайте, чтобы две базы данных считали себя владельцами одних и тех же данных. Этот путь ведет к кошмарам согласованности, которые почти невозможно отладить.

Соображения по миграции

Если вы поняли, что выбрали не ту базу данных, миграция возможна, но дорога. Вот практические соображения:

Миграция с MongoDB на PostgreSQL — самая распространенная миграция, которую я видел. Обычная причина заключается в том, что приложение выросло за пределы документных запросов и потребовало сложных соединений, транзакций или агрегаций. Миграция включает в себя проектирование реляционной схемы, написание скриптов преобразования и обновление каждого запроса к базе данных в приложении. Заложите от двух до четырех недель на умеренно сложное приложение.

Миграция с PostgreSQL на MongoDB встречается реже, но происходит, когда модель данных приложения становится преимущественно документоориентированной. Миграция механически проще (преобразование таблиц в документы), но требует переосмысления каждого запроса и потери гарантий транзакций.

Миграция с Firebase на PostgreSQL — самая сложная, потому что это не просто изменение базы данных — это изменение архитектуры. Вам нужно создать уровень API, реализовать аутентификацию, заменить слушатели реального времени на WebSockets или опрос, а также обрабатывать автономную синхронизацию. Это ближе к переписыванию, чем к миграции.

Лучшая миграция — та, которую вы избежали. Потратьте лишний день на обдумывание вашей модели данных заранее. Поговорите с тем, кто создавал похожее приложение. Стоимость выбора правильной базы данных изначально всегда меньше, чем стоимость миграции позже.

Анализ стоимости

Стоимость баз данных в масштабе может быть удивительной, особенно с управляемыми сервисами.

Сервис Бесплатный уровень Малое производство Среднее производство
Supabase (PostgreSQL) 500 МБ, 2 проекта ~$25/мес (8 ГБ, 2 ядра) ~$100/мес (32 ГБ, 4 ядра)
Neon (PostgreSQL) 0.5 ГБ хранилища ~$19/мес ~$69/мес
MongoDB Atlas 512 МБ общее ~$57/мес (M10 выделенный) ~$200/мес (M30)
Firebase Firestore 1 ГБ хранилища, 50 тыс. чтений/день ~$25-100/мес (сильно варьируется) $100-1000/мес (зависит от запросов)
Redis Cloud 30 МБ ~$7/мес (25
DU

Danil Ulmashev

Full Stack Developer

Хотите работать вместе?