MongoDB: что это, зачем нужна и когда не нужна

✓ Все примеры выполнены на MongoDB 8.2.6, драйвер mongodb 7.5.0, Node.js v22.23.1 — июль 2026

Содержание

MongoDB — самая популярная документоориентированная база данных, и почти вся путаница вокруг неё возникает из-за одного вопроса: чем документ отличается от строки таблицы и что это меняет на практике.

Документ вместо строки

MongoDB хранит объекты целиком, а не разложенными по таблицам.

В реляционной базе пользователь с адресом и телефонами — это три таблицы и JOIN при каждом чтении. В MongoDB это один документ:

{
  _id: ObjectId('6a59e5dd7368400e383caa62'),
  name: 'Аня',
  age: 30,
  city: 'Москва',
  теги: ['vip'],
  адрес: { улица: 'Тверская', дом: 7 }
}

Вложенные объекты и массивы — обычные значения полей. Прочитать такого пользователя — одно обращение к базе: всё, что нужно, лежит в одном месте на диске.

Отсюда и вся выгода, и вся расплата. Выгода: чтение цельной сущности быстрое и простое. Расплата: если те же данные нужны в другом разрезе — например, «все пользователи с улицы Тверской», — придётся либо индексировать вложенное поле, либо дублировать данные.

Словарь для перехода с SQL:

SQL MongoDB
таблица коллекция
строка документ
колонка поле
первичный ключ _id
JOIN вложенность или $lookup (дороже)
схема таблицы отсутствует
Одна сущность: как её хранят SQL и MongoDB

SQL: три таблицы

Пользователь разложен по таблицам, читается JOIN-ом

  • users — имя, возраст
  • addresses — улица, дом, FK на users
  • phones — номер, FK на users
  • Чтение профиля = JOIN трёх таблиц
  • Схема жёсткая: новое поле = ALTER TABLE

MongoDB: один документ

Всё, что нужно, лежит рядом на диске

  • { name, age, адрес: {…}, телефоны: […] }
  • Чтение профиля = одно обращение
  • Вложенность — обычное значение поля
  • Схемы нет: новое поле = просто пишем
  • Цена: тот же набор в другом разрезе — дублирование

Схемы действительно нет

Это не маркетинг, а буквальное поведение. Положим в одну коллекцию документы разной формы:

await users.insertOne({ name: 'Аня', age: 30, city: 'Москва' });
await users.insertOne({ name: 'Егор', любимыйЦвет: 'синий', теги: ['a', 'b'] });
наборы полей у документов:
   name, age, city
   name, age
   name, age, city
   name, любимыйЦвет, теги

База не возразила. Ни один документ не «неправильный».

Что это даёт. Добавить поле — просто начать его писать. Никаких миграций, никакого ALTER TABLE на таблице в миллион строк, никакого простоя. Для данных, форма которых меняется или заранее неизвестна, это огромная экономия.

Чем это грозит. Схема никуда не делась — она просто переехала из базы в ваш код. Написали usre вместо user — документ запишется, а запрос по user его не найдёт. База не поможет: для неё оба поля одинаково законны.

Поэтому в реальных проектах схему возвращают: либо Mongoose со схемами и валидацией, либо $jsonSchema в валидаторе коллекции — то есть средствами самой MongoDB. Вопрос лишь в том, где вы её опишете.

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

  • Форма данных разнородна. Каталог товаров, где у обуви размер, у ноутбука диагональ, а у книги ISBN. В SQL это либо десятки почти пустых колонок, либо таблица «атрибутов» с самодельным типизированием.
  • Схема меняется часто. Продукт на ранней стадии: сегодня у сущности пять полей, через месяц восемь.
  • Данные читаются целиком. Профиль, заказ, карточка — всё, что запрашивают одним куском.
  • Естественная вложенность. Комментарии внутри поста, позиции внутри заказа.
  • Горизонтальное масштабирование заложено изначально. Шардирование в MongoDB встроено, а не приделано сбоку.

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

Честный список важнее списка достоинств:

  • Много связей. Если данные постоянно соединяются в разных комбинациях — это профиль реляционной базы. $lookup в MongoDB есть, но он дороже JOIN и это признак того, что модель выбрана неверно.
  • Жёсткие требования к целостности. Финансы, учёт, всё, где недопустима запись «неправильной» формы. Внешние ключи и NOT NULL в SQL делает сама база — здесь их придётся писать руками.
  • Сложная аналитика. Агрегации по многим сущностям — работа для SQL. Aggregation pipeline в MongoDB мощный, но многословный.
  • Транзакции — норма, а не исключение. Они есть с версии 4.0 и с ACID-гарантиями, но обходятся дороже: модель предполагает, что связанные данные лежат в одном документе и транзакция не нужна.

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

И честная оговорка про «MongoDB быстрее»: чаще всего скорость определяют индексы и модель данных, а не название базы. Запрос без индекса будет медленным в любой СУБД.

Индексы обязательны — так же, как в SQL

Распространённое заблуждение: раз базa «нереляционная», то и работает иначе. Не работает. Проверим на коллекции в 20 000 документов:

const plan = await big.find({ num: 19999 }).explain('executionStats');
БЕЗ индекса: этап = COLLSCAN | просмотрено документов = 20000 | мс = 7
С индексом : этап = FETCH    | просмотрено документов = 1     | мс = 0
20 000
Документов читает база без индекса
ради одной записи
Источник: собственный замер, MongoDB 8.2.6
1
С индексом
тот же запрос
Источник: собственный замер, MongoDB 8.2.6

COLLSCAN — полный перебор коллекции. Ровно как Seq Scan в PostgreSQL. Подробнее — в разборе CRUD-операций.

Главное решение — не «какая база», а как разложить данные

В MongoDB нет JOIN, поэтому связь между сущностями вы решаете на этапе проектирования, и переиграть потом дорого. Развилок ровно две.

Вложить (embed) — положить связанные данные внутрь документа:

{ _id: 1, title: 'Пост', комментарии: [ { автор: 'Аня', текст: '…' } ] }

Читается одним запросом, всё рядом. Но: документ ограничен 16 МБ, а тысяча комментариев поедет к клиенту вместе с постом, даже если нужен был только заголовок.

Сослаться (reference) — хранить id и собирать вторым запросом:

{ _id: 1, title: 'Пост' }
{ _id: 99, postId: 1, автор: 'Аня', текст: '…' }

Документы остаются маленькими, комментариев может быть сколько угодно. Цена — два запроса или $lookup.

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

Ошибка новичка — вкладывать всё подряд, потому что «так же удобно». Через год документ на 12 МБ, и каждое чтение профиля тянет всю историю активности.

Что было на этой странице в 2017 году

Первая версия вышла 13 августа 2017 года, когда актуальной была MongoDB 3.x. Основное объяснение — документы вместо таблиц, отсутствие схемы — не устарело: модель данных с тех пор не менялась.

Устарел контекст спора. В 2017-м вокруг MongoDB ещё висела репутация «базы, которая теряет данные» — наследие ранних версий, где запись по умолчанию не дожидалась подтверждения сервера. К современным версиям эта критика не относится: writeConcern по умолчанию требует подтверждения записи.

И главное: в 2017-м у MongoDB не было многодокументных транзакций — они появились в версии 4.0, в 2018-м. Это был сильнейший аргумент против неё, и статья того времени честно писала о нём как о данности. Сегодня аргумент снят: транзакции есть. Но выбор между MongoDB и PostgreSQL от этого не стал проще — он просто перестал быть спором о возможностях и стал вопросом модели данных.


Смежные темы: CRUD-операции MongoDB — весь синтаксис с замерами; вставка документов; промисы — почему драйвер асинхронный. Полный список — в справочнике по Node.js.

Частые вопросы

MongoDB быстрее PostgreSQL?
Нет универсального ответа. На чтении цельного документа по ключу MongoDB обычно быстрее — данные лежат рядом. На сложных соединениях и агрегациях по многим таблицам PostgreSQL выигрывает. Скорость определяют индексы и модель данных, а не название базы.
В MongoDB есть транзакции?
Есть, начиная с версии 4.0 — многодокументные и с ACID-гарантиями. Но они дороже, чем в реляционной базе, и сама модель предполагает, что связанные данные лежат в одном документе и транзакция не нужна.
Правда, что MongoDB теряет данные?
Это наследие ранних версий, где запись по умолчанию не подтверждалась. Сегодня writeConcern по умолчанию требует подтверждения, и та критика к текущим версиям не относится.
Можно ли обойтись без Mongoose?
Да, официальный драйвер самодостаточен. Mongoose нужен ради схем и валидации — то есть ради того, что MongoDB намеренно не делает.
Подходит ли MongoDB для финансов и учёта?
Как правило, нет. Там жёсткая схема, связи и транзакции — это профиль реляционной базы. MongoDB хороша там, где форма данных разнородна или меняется.