MongoDB или PostgreSQL — что лучше выбрать для проекта

MongoDB и PostgreSQL часто сравнивают как “гибкость против строгости”, но в реальных проектах всё интереснее. Разбираемся, какую базу выбрать, чтобы потом не страдать на миграциях, отчетах и внезапных требованиях бизнеса.

Обычно вопрос “MongoDB или PostgreSQL?” появляется не тогда, когда проект уже спокойно работает, а в самом начале, когда всё выглядит простым. Есть пользователи, товары, заказы, комментарии — ну база и база. Хочется выбрать что-то “современное”, быстро начать писать код и не тратить неделю на проектирование схемы.

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

MongoDB и PostgreSQL обе нормальные, взрослые технологии. Проблема не в том, что одна “плохая”, а другая “хорошая”. Проблема в том, что их часто выбирают не под задачу, а по настроению: “MongoDB проще”, “PostgreSQL надежнее”, “NoSQL быстрее”, “SQL устарел”. Спойлер: такие формулировки обычно плохо заканчиваются.

Если совсем коротко

PostgreSQL чаще стоит выбирать по умолчанию для большинства backend-проектов: интернет-магазинов, CRM, личных кабинетов, финансовых операций, SaaS-сервисов, админок, систем с отчетами и сложными связями между данными.

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

Но важный нюанс: “у меня JSON” ещё не значит “мне нужна MongoDB”. PostgreSQL тоже умеет работать с JSON и JSONB. В документации PostgreSQL прямо указано, что jsonb хранится в бинарном разобранном виде и обычно быстрее обрабатывается, чем обычный json, которому нужно заново парсить текст при выполнении операций.

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

В чем главная разница

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

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

То есть PostgreSQL чаще говорит: “Давай аккуратно разложим данные по полочкам и будем связывать их запросами”.
MongoDB говорит: “Давай положим рядом всё, что нужно получить одним запросом”.

Оба подхода нормальные. Просто они решают разные боли.

Когда PostgreSQL обычно лучше

PostgreSQL особенно хорошо чувствует себя в проектах, где данные имеют понятную структуру и между ними много связей. Например, интернет-магазин. Есть пользователи, заказы, товары, скидки, платежи, возвраты, склады. Здесь почти сразу появляются связи “один ко многим”, “многие ко многим”, ограничения целостности и отчеты.

На практике это выглядит так: сначала заказ кажется простым объектом, но потом бизнес просит показать средний чек по регионам, посчитать повторные покупки, найти товары, которые чаще всего покупают вместе, отфильтровать заказы по статусу оплаты и способу доставки. SQL в таких задачах не мешает, а помогает. Он как раз для этого и придуман.

PostgreSQL также хорош там, где важна надежность операций. Например, нужно списать деньги, создать заказ и уменьшить остаток товара на складе. Такие действия нельзя делать “примерно”. Если одна часть операции прошла, а другая упала, данные превращаются в кашу. В PostgreSQL транзакции строятся через BEGIN и COMMIT, и это базовый механизм для группировки нескольких SQL-команд в одну логическую операцию.

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

Когда MongoDB действительно удобнее

MongoDB раскрывается там, где структура данных гибкая и не хочется насильно загонять всё в таблицы. Например, каталог товаров. У ноутбука есть процессор, диагональ экрана, объем оперативной памяти. У кроссовок — размер, материал, сезон. У книги — автор, ISBN, издательство. Формально это всё “товары”, но набор характеристик у них разный.

В PostgreSQL такую модель тоже можно сделать, но иногда она получается тяжеловатой: отдельные таблицы характеристик, связи, EAV-модель, куча джойнов. В MongoDB можно хранить товар как документ с вложенными характеристиками. Если приложение чаще читает карточку товара целиком, такой подход выглядит естественно.

Например, документ товара может выглядеть так:

{
"_id": "product_1001",
"title": "Игровой ноутбук",
"category": "Ноутбуки",
"price": 129000,
"specs": {
"cpu": "Ryzen 7",
"ram": "32 GB",
"display": "15.6 inch",
"gpu": "RTX 4070"
},
"tags": ["gaming", "laptop", "high-performance"]
}

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

Но есть подвох. Гибкая схема не означает “схемы нет”. Она просто живет не только в базе, но и в коде приложения. И если команда не следит за структурой документов, через год в коллекции могут оказаться поля userId, user_id, uid и clientId, которые вроде означают одно и то же, но уже никто не уверен. На практике это не свобода, а маленький музей археологии backend-решений.

А что насчет скорости?

Очень частый вопрос: “Что быстрее — MongoDB или PostgreSQL?” Ответ скучный, но честный: зависит от модели данных, индексов, запросов и нагрузки.

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

PostgreSQL может быть очень быстрым на сложных выборках, агрегациях, отчетах и связях. Особенно если нормально настроены индексы и запросы не написаны в стиле “ну ORM сам разберется”. ORM, кстати, не всегда разбирается. Иногда он генерирует такой SQL, что хочется выключить ноутбук и уйти в столярку.

Главная мысль простая: скорость базы — это не только выбор технологии. Плохую схему можно сделать и в PostgreSQL, и в MongoDB. Просто в PostgreSQL ошибки часто проявляются как “запрос стал медленным”, а в MongoDB — как “мы теперь не можем нормально собрать данные без костылей”.

Пример: пользователи и заказы

Представим обычный backend на Node.js. Есть пользователь и его заказы.

В PostgreSQL это обычно выглядело бы так:

CREATE TABLE users (
id SERIAL PRIMARY KEY,
email TEXT UNIQUE NOT NULL,
name TEXT NOT NULL,
created_at TIMESTAMP DEFAULT NOW()
);

CREATE TABLE orders (
id SERIAL PRIMARY KEY,
user_id INTEGER NOT NULL REFERENCES users(id),
total_price NUMERIC(10, 2) NOT NULL,
status TEXT NOT NULL,
created_at TIMESTAMP DEFAULT NOW()
);

Здесь база сама следит, чтобы заказ не ссылался на несуществующего пользователя. Это очень важная вещь. Не приложение “пообещало проверить”, а база гарантирует.

Запрос заказов пользователя будет понятным:

SELECT 
orders.id,
orders.total_price,
orders.status,
orders.created_at
FROM orders
WHERE orders.user_id = 1
ORDER BY orders.created_at DESC;

А если нужно получить пользователя вместе с заказами:

SELECT 
users.email,
users.name,
orders.id AS order_id,
orders.total_price,
orders.status
FROM users
JOIN orders ON orders.user_id = users.id
WHERE users.id = 1;

Пояснение простое: если данные связаны, PostgreSQL позволяет явно описать эти связи и потом удобно по ним ходить.

В MongoDB можно было бы хранить пользователя отдельно, а заказы отдельно:

// users
{
"_id": "user_1",
"email": "alex@example.com",
"name": "Алексей"
}

// orders
{
"_id": "order_1",
"userId": "user_1",
"totalPrice": 4900,
"status": "paid"
}

Можно даже вложить заказы прямо в пользователя:

{
"_id": "user_1",
"email": "alex@example.com",
"name": "Алексей",
"orders": [
{
"id": "order_1",
"totalPrice": 4900,
"status": "paid"
}
]
}

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

PostgreSQL тоже умеет быть гибким

Один из частых аргументов в пользу MongoDB звучит так: “У нас данные могут меняться, значит нужна документная база”. Иногда да. Но не всегда.

PostgreSQL поддерживает типы json и jsonb, а также функции и операторы для работы с JSON внутри SQL. В официальной документации PostgreSQL описано, что SQL/JSON-модель позволяет работать с JSON-структурами, массивами и объектами прямо в SQL-среде.

Например, можно сделать таблицу товаров так:

CREATE TABLE products (
id SERIAL PRIMARY KEY,
title TEXT NOT NULL,
price NUMERIC(10, 2) NOT NULL,
specs JSONB NOT NULL
);

И сохранить разные характеристики:

INSERT INTO products (title, price, specs)
VALUES (
'Игровой ноутбук',
129000,
'{"cpu": "Ryzen 7", "ram": "32 GB", "gpu": "RTX 4070"}'
);

Такой подход часто удобен в реальных проектах. Основные поля, по которым нужны фильтры, связи и ограничения, остаются нормальными колонками. А гибкие характеристики живут в jsonb.

Это не значит, что PostgreSQL превращается в MongoDB. Но это значит, что выбор не всегда бинарный. Иногда лучший вариант — PostgreSQL как основная база плюс JSONB для гибких частей.

Типичные ошибки при выборе

Самая частая ошибка с MongoDB — выбирать её только потому, что “не надо писать схему”. В маленьком pet-проекте это приятно. В командной разработке может быстро стать проблемой. Схема всё равно появится, просто она будет размазана по коду, валидаторам, DTO, frontend-формам и документации, которую никто не обновляет.

Вторая ошибка — хранить в MongoDB всё вложенным, потому что “так удобно”. На старте правда удобно. Но если вложенный массив начинает расти бесконечно, документ становится тяжелым, обновления усложняются, а запросы превращаются в гимнастику.

С PostgreSQL другая типичная ошибка — слишком рано усложнять модель. Иногда разработчик создает 15 таблиц там, где можно было начать с 4. Да, нормализация полезна, но не надо превращать каждую строку в отдельную сущность только потому, что “так академически правильно”.

Еще одна боль — слепая вера в ORM. В PostgreSQL это особенно заметно: разработчик пишет красивый код на уровне моделей, а под капотом летит пачка неэффективных запросов. Поэтому хотя бы базовый SQL знать нужно. Даже если вы пишете на Prisma, TypeORM, Sequelize или Django ORM.

Что выбрать для разных проектов

Для интернет-магазина, CRM, ERP, SaaS-сервиса, финансового учета, бронирований, системы подписок или любого проекта с большим количеством связей я бы почти всегда начинал с PostgreSQL.

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

Для стартапа, где пока непонятна модель данных, я бы всё равно не спешил автоматически брать MongoDB. Если есть платежи, пользователи, тарифы, роли и отчеты — PostgreSQL даст больше опоры. Если же продукт крутится вокруг гибких документов, пользовательских форм или контента с разной структурой — MongoDB выглядит логичнее.

А можно использовать обе?

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

Но для первого проекта я бы не советовал сразу тащить две базы. Это не “профессионально”, это часто просто лишняя сложность. Нужно поддерживать две модели данных, два способа бэкапов, два набора индексов, два подхода к мониторингу. Если команда маленькая, такая архитектура может съесть больше времени, чем сэкономить.

Лучше начать с одной базы, которая закрывает 80–90% задач проекта. А вторую добавлять только тогда, когда появилась понятная причина, а не желание сделать “как в больших компаниях”.

Итог

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

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

На практике я бы формулировал выбор так: если вы можете заранее описать основные сущности и связи между ними — берите PostgreSQL. Если данные больше похожи на набор самостоятельных документов с разной внутренней структурой — смотрите в сторону MongoDB.

И главное: не выбирайте базу по хайпу. База данных — это не просто место хранения. Это фундамент проекта. А фундамент лучше делать не “модным”, а таким, чтобы через полгода не хотелось всё переписывать с выражением лица человека, который только что открыл старый код без документации.

Последние статьи