Документоориентированные хранилища данных: MongoDB
В реляционных базах данных и строгих колоночных форматах Parquet каждая таблица имеет жесткую предопределенную схему (Schema-on-Write). Все строки обязаны иметь одинаковый набор столбцов, а добавление нового атрибута требует операции ALTER TABLE. Однако в реальных прикладных системах городского хозяйства и интернета вещей данные часто обладают высокой структурной неоднородностью.
Карточка аварийной заявки диспетчерской службы содержит базовые поля (адрес, время, заявитель), но состав специфических атрибутов зависит от характера происшествия: для прорыва трубопровода важны диаметр трубы, глубина залегания и давление, для отказа электроподстанции — фаза, напряжение и схема резервирования, а для заявки по лифтовому оборудованию — этажность, модель лебедки и коды ошибок контроллера. Попытка уложить такие данные в классическую реляционную схему приводит либо к созданию десятков полупустых таблиц, либо к антипаттерну Entity-Attribute-Value (EAV) с катастрофической потерей производительности 1.
Естественным решением для таких задач стал класс документоориентированных СУБД (Document Stores), ярким представителем которых является MongoDB 2.
Модель данных: документы и формат BSON
В MongoDB базовой единицей хранения является документ (Document), а совокупность документов образует коллекцию (Collection), аналогичную таблице реляционной СУБД.
Документы представлены в формате BSON (Binary JSON) — бинарной сериализации объектов JSON. В отличие от стандартного текстового JSON, формат BSON:
- Поддерживает расширенный набор типов данных: 64-битные целые (
int64), даты с миллисекундной точностью (Date), бинарные массивы (binData), регулярные выражения и уникальные 12-байтные идентификаторыObjectId. - Явно сохраняет длину каждого поля и документа, что позволяет СУБД пропускать ненужные поля при парсинге без последовательного посимвольного чтения.
- Имеет жесткое ограничение на максимальный размер одного документа: 16 МБ (для хранения больших файлов вроде сканов актов и схем применяется протокол GridFS).
Пример документа аварийного инцидента в BSON:
{
"_id": ObjectId("65e0a1b2c3d4e5f6a7b8c9d0"),
"incident_number": "INC-2024-8841",
"created_at": ISODate("2024-03-01T14:22:00Z"),
"district": "Советский",
"location": {
"type": "Point",
"coordinates": [84.9521, 56.4883]
},
"category": "водоснабжение",
"equipment": {
"pipe_diameter_mm": 300,
"material": "чугун",
"depth_m": 2.4
},
"tags": ["срочно", "утечка", "проезжая_часть"],
"inspections": [
{
"inspector": "Иванов А. В.",
"date": ISODate("2024-03-01T15:10:00Z"),
"result": "требуется раскопка"
}
],
"status": "в_работе"
}
Встраивание (Embedding) против ссылок (Referencing)
В реляционных базах связь между сущностями реализуется через внешние ключи и нормализацию. В документоориентированных базах разработчик выбирает между двумя стратегиями:
- Встраивание документов и массивов (Embedding):
Связанные данные хранятся прямо внутри родительского документа (например, массив актов осмотра
inspectionsили координатыlocation).- Плюсы: Чтение всей информации об инциденте выполняется за одно дисковое обращение без соединений (JOIN). Атомарность обновления всего документа гарантируется из коробки.
- Минусы: Дублирование данных при связях многие-ко-многим; риск превышения лимита 16 МБ при неконтролируемом росте массивов.
- Ссылки по идентификатору (Referencing):
Хранение
_idсвязанного документа из другой коллекции (например, ссылка на карточку обслуживающей организации). Используется при редких обращениях или очень больших объемах связанных данных.
Индексирование в MongoDB
Хранение документов на диске в движке WiredTiger организовано на основе B-деревьев. Для ускорения поиска MongoDB поддерживает богатый набор индексов:
- Одиночные и составные индексы (Single / Compound Indexes):
Позволяют быстро находить документы по значениям одного или нескольких полей:
db.incidents.createIndex({ district: 1, created_at: -1 }); - Многоключевые индексы (Multikey Indexes):
Если индекс строится по полю, содержащему массив (например,
tags: ["срочно", "утечка"]), MongoDB автоматически индексирует каждый элемент массива. Запросdb.incidents.find({ tags: "срочно" })использует индекс по B-дереву. - Геопространственные индексы (2dsphere):
Специализированные индексы для работы с координатами стандарта GeoJSON на сферической поверхности Земли. Поддерживают поиск ближайших объектов (
$nearSphere) и проверку попадания в полигон ($geoWithin):db.incidents.createIndex({ location: "2dsphere" }); - Текстовые индексы (Text Indexes): Обеспечивают полнотекстовый поиск по ключевым словам с учетом стемминга и стоп-слов.
Конвейер агрегации (Aggregation Pipeline)
В отличие от простых операций поиска find(), для сложного аналитического анализа в MongoDB используется конвейер агрегации (Aggregation Pipeline) 3. Документы последовательно проходят через цепочку стадий преобразования, аналогично конвейеру UNIX:
Коллекция --> [$match] --> [$unwind] --> [$group] --> [$sort] --> Результат
Основные стадии конвейера
$match: Фильтрация документов по предикату (аналогWHEREв SQL). Если$matchстоит первым этапом, он эффективно использует B-дерево индексы.$project: Выборка, переименование полей или вычисление новых выражений (аналогSELECT).$group: Группировка документов по ключу_idс вычислением агрегатных функций:$sum,$avg,$min,$max,$push(аналогGROUP BY).$unwind: Разворачивание массива в поток отдельных документов. Если документ содержал массив из 3 элементов, стадия$unwindпорождает 3 копии документа, каждая с одним элементом массива.$lookup: Левое внешнее соединение (Left Outer Join) с документами из другой коллекции.$facet: Выполнение нескольких независимых агрегационных конвейеров над одним набором данных в рамках одного запроса (удобно для фасетного поиска).
Пример агрегационного конвейера для расчета распределения типов аварий по районам:
db.incidents.aggregate([
{ $match: { status: "завершено", created_at: { $gte: ISODate("2024-01-01") } } },
{ $group: {
_id: { district: "$district", category: "$category" },
incident_count: { $sum: 1 },
avg_repair_hours: {
$avg: {
$divide: [{ $subtract: ["$closed_at", "$created_at"] }, 3600000]
}
}
}},
{ $sort: { incident_count: -1 } }
]);
Транзакции и архитектура высокой доступности
Исторически документные базы критиковали за отсутствие гарантий ACID для нескольких документов. Однако начиная с версии 4.0 MongoDB поддерживает многодокументные распределенные транзакции (Multi-Document ACID Transactions) с изоляцией Snapshot Isolation на базе движка WiredTiger 4.
Для обеспечения отказоустойчивости применяется механизм наборов реплик (Replica Sets):
- Один первичный узел (Primary), принимающий все операции записи.
- Несколько вторичных узлов (Secondary), асинхронно или синхронно реплицирующих журнал операций (oplog).
- При отказе Primary вторичные узлы автоматически проводят голосование по алгоритму Raft и выбирают нового лидера менее чем за 3 секунды.
Для горизонтального масштабирования объемов хранения применяется шардирование (Sharding) на основе ключа шардирования (Shard Key) и маршрутизаторов mongos.
Итоги темы
MongoDB и документоориентированная модель представляют собой мощный инструмент для систем с динамической и слабоструктурированной природой данных. Формат BSON объединяет выразительность JSON с эффективностью бинарного представления, богатая система индексов (включая геопространственные 2dsphere) обеспечивает высокую скорость точечных запросов, а конвейер агрегации Aggregation Pipeline позволяет решать сложные аналитические задачи прямо внутри распределенной СУБД.
Литература
- Fowler M., Sadalage P. J. NoSQL Distilled: A Brief Guide to the Emerging World of Polyglot Persistence. — Addison-Wesley Professional, 2012. — Chapter 3: Document Databases 1.
- Chodorow K. MongoDB: The Definitive Guide: Powerful and Scalable Data Storage. — 3rd ed. — O'Reilly Media, 2020 2.
- MongoDB Documentation. The Aggregation Pipeline Architecture. — MongoDB Inc., 2024. https://www.mongodb.com/docs/manual/core/aggregation-pipeline/ 3.
- MongoDB Documentation. WiredTiger Storage Engine and Concurrency Control. — MongoDB Inc., 2024. https://www.mongodb.com/docs/manual/core/wiredtiger/ 4.