Самый частый антипаттерн в выборе БД — «возьмём ту, к которой привыкли». Вторая по частоте — «возьмём MongoDB, потому что у нас же микросервисы / схема будет меняться / NoSQL модно». Оба подхода — про доступность инструмента, не про природу задачи.
Эта статья — десять конкретных критериев, по которым выбор PG vs Mongo становится не вопросом вкуса. Примеры — на тех же двух сущностях, что и в разделах про PostgreSQL и про MongoDB (category + product).
Десять критериев
1. Природа данных
PG лучше, если данные образуют граф связей: заказ ссылается на клиента и магазин, у магазина категории, у категорий товары, у товара — отзывы и история цен. JOIN-ы между сущностями — частая операция.
Mongo лучше, если у вас агрегаты, читающиеся целиком: профиль пользователя со всеми настройками; заказ со всеми позициями; документ с динамической схемой (товары с разными атрибутами для разных категорий).
Тест: представьте типичный запрос. Если он начинается с «найди X и подтяни связанные Y, Z, W» — PG. Если «возьми один документ X со всем содержимым» — Mongo.
2. Шаблон запросов
PG лучше при разнообразии запросов: REST + аналитика + отчёты + ad-hoc от бизнеса. SQL — универсальный язык, оптимизатор сам решит, как соединить таблицы.
Mongo лучше при «известных вперёд» запросах. Schema design в MongoDB подгоняется под запросы: вы embed'ите то, что читается вместе. Если запросы непредсказуемы — embed-структуры становятся не оптимальными, и вы либо денормализуете всё, либо городите $lookup.
3. Транзакции
PG лучше, если нужны транзакции на нескольких сущностях постоянно. Перевод денег, списание остатка с проверкой лимита, заказ с обновлением баланса и каталога — это нормальная нагрузка для PG.
Mongo приемлемо, если транзакции редкие и короткие. Multi-document transactions работают с 4.0, но дороже чем в PG (мин. время удержания 60 сек, лимит oplog'а 16 MB). Если 50% операций требуют транзакций — это сигнал, что схема изначально должна была быть в PG.
4. Жёсткость схемы
PG лучше, если структура данных понятна и стабильна. Схема — это документация и контракт между сервисами. NOT NULL, FK, CHECK ловят ошибки до production.
Mongo лучше, если структура меняется часто: каталоги с разнородными товарами (электроника / одежда / еда у каждой свои поля), форма с динамическими полями, event log с разными типами событий. JSON Schema validator в MongoDB добавляет контроль, но он опциональный.
Антипаттерн «возьмём Mongo, потому что схема может меняться» — обычно красный флаг: если схема меняется хаотично, это не свойство БД, а отсутствие моделирования. Лучше сначала смоделировать домен (см. DDD: тактические паттерны).
5. Объём данных и шардинг
PG до ~500 GB на одной машине — без проблем. Партиционирование внутри одной БД (RANGE/LIST/HASH) снимает 80% типичных болей с большими таблицами. Шардирование между серверами — через Citus или ручную инфраструктуру, дорого.
Mongo сразу из коробки делает горизонтальное масштабирование. Sharded cluster — стандартный режим работы крупных кластеров. Если заранее ясно, что данных будет десятки терабайт и больше одного сервера — Mongo проще.
6. Read/write пропорции
PG лучше, если read-write примерно равные с явными транзакционными границами. Аналитические запросы хорошо ложатся на индексы PG.
Mongo лучше, если много чтения и read-after-write критичен в географически распределённой системе. Replica set + causal consistency + read preference дают гибкую балансировку. PG read-replica работают, но read-after-write требует подписки на slot и больше операционной работы.
7. Тип основной задачи
| Задача | Что лучше |
|---|---|
| ERP, биллинг, бухгалтерия — много инвариантов на нескольких таблицах | PG |
| Каталог товаров с разными атрибутами по категориям | Mongo (или PG + jsonb) |
| Лента событий / log / трейсы | Mongo, ClickHouse, или Kafka + ad-hoc query layer |
| Сессии, кэш, временные данные | Redis, не Mongo и не PG |
| Поиск с full-text + facets | OpenSearch / Elasticsearch, иногда + PG (tsvector) |
| Аналитика, OLAP-агрегаты | ClickHouse / DuckDB, не Mongo и не PG |
| CRUD-сервис с понятной схемой и FK | PG почти всегда |
| Профиль пользователя с глубоким json | Mongo или PG + jsonb |
8. PostgreSQL + jsonb — третья опция
Часто упускают: в PG есть тип jsonb с индексами (GIN), операторами поиска внутри json, partial-индексами на полях. Для смешанных случаев (связи + динамические атрибуты) PG + jsonb даёт 80% преимуществ Mongo + сохраняет ACID, FK, JOIN-ы. Когда искренний выбор «жёсткие связи или гибкость» — это часто выбор внутри PG, а не между PG и Mongo.
-- товар с произвольными атрибутами в одной таблице
CREATE TABLE product (
id BIGSERIAL PRIMARY KEY,
category_id BIGINT REFERENCES category(id),
price NUMERIC(10,2) NOT NULL,
name TEXT NOT NULL,
attributes JSONB NOT NULL DEFAULT '{}'::jsonb
);
CREATE INDEX product_attributes_gin ON product USING GIN (attributes);
-- Запрос: товары с цветом "красный"
SELECT * FROM product WHERE attributes @> '{"color": "red"}';
Если задача звучит как «нам нужна гибкость атрибутов» — попробуйте jsonb до того, как уйдёте в Mongo.
9. Операционная зрелость команды
PG в команде Java/Spring — обычно естественный выбор: jOOQ/JPA знают все, миграции через Flyway/Liquibase отработаны, мониторинг через pg_stat_* и pgAdmin понятен. Резервное копирование, репликация, restore — десятки лет практики.
Mongo требует другого набора навыков: индексы строятся по другим правилам, шардинг — отдельная инженерная дисциплина, мониторинг другой (Atlas / MongoDB Ops Manager), бэкап sharded cluster — нетривиальная процедура. Если в команде нет инженера с реальным production-опытом в Mongo — это серьёзный риск, особенно при первой проблеме на проде.
10. Экосистема и инструменты
PG: огромная экосистема расширений (PostGIS для гео, TimescaleDB для time-series, pg_partman для партиционирования, pgvector для векторного поиска, Citus для шардинга). Часто PG заменяет 3–4 специализированные БД одним сервером.
Mongo: Atlas как managed-сервис, Atlas Search (на Lucene), Atlas Charts для BI, Stream Processing, Time Series Collections. Платная зрелая экосистема, но привязка к Atlas.
Чек-лист «возьми бал»
Поставьте 1 балл за каждое «да»:
- ☐ Между сущностями много связей, и JOIN-ы — частая операция.
- ☐ Транзакции на нескольких сущностях — не редкость.
- ☐ Запросы непредсказуемы (BI, аналитика, ad-hoc).
- ☐ Команда знает PG и не знает Mongo.
- ☐ Объём данных в пределах одного сервера на ближайшие 2–3 года.
- ☐ Нужны FK, CHECK, NOT NULL для контроля целостности.
- ☐ Есть гео / time-series / векторный поиск (расширения PG).
| Баллов | Выбор |
|---|---|
| 0–2 | Серьёзно рассмотрите MongoDB |
| 3–4 | PG, но проверьте задачу на jsonb-кейс или гибрид |
| 5–7 | PostgreSQL |
Типичные ошибки выбора
«Mongo — потому что NoSQL». NoSQL — не свойство, а отсутствие свойства. Если задача — нормально нормализованные данные с инвариантами, NoSQL ничего не даёт, только убирает гарантии.
«PG — потому что у всех PG». Если домен — лента событий, каталог разнородных товаров, профили с deep-nested данными, и команда знает Mongo — упирать в PG из инерции дорого.
«Mongo, чтобы избежать миграций». Миграции схемы в Mongo всё равно нужны (новое обязательное поле, переименование, изменение типа). Их просто никто не пишет, и через год разработчики не знают, какие документы валидны.
«PG + jsonb всегда заменит Mongo». Не всегда: при глубокой вложенности (3–5 уровней массивов) запросы по jsonb становятся менее читаемыми и медленнее, чем эквивалент в Mongo. Mongo даёт лучший developer experience для документной модели как первого класса.
«Возьмём обе, чтобы было гибче». Двойная операционная нагрузка, два бэкапа, два мониторинга, два набора миграций, риск рассинхрона. Берут две БД только когда природа задач реально разная (например, OLTP в PG + event log в Mongo).
Когда обе БД в одном сервисе — нормально
Не «возьмём обе на всякий случай», а конкретные комбинации:
- PG для OLTP (заказы, баланс, каталог), Mongo для аудит-лога или фидов событий. Транзакционные данные — в PG; растущий лог без сложных запросов — в Mongo.
- PG для реляционных данных, Mongo (или Redis) для динамических настроек пользователей / фич-флагов / A/B.
- PG как источник правды, Mongo как поисковый индекс через CDC (Debezium → Mongo). Запросы на чтение идут в Mongo, источник — PG.
Антипаттерн: dual-write в обе БД из приложения без CDC и без outbox. Получите расхождение в первый же месяц.
Что почитать дальше
- Раздел про PostgreSQL — ACID, репликация, партиционирование/шардирование, размер БД.
- Раздел про MongoDB — concerns, replica set, sharded cluster, моделирование документов.
- Монолит, модульный монолит или микросервисы — соседняя статья про другой архитектурный выбор.
- Distributed Patterns Style Guide — saga, outbox, idempotency: нужны при работе с обеими БД одновременно.