Модели NoSQL: ключ-значение, документы, колонки, графы
NoSQL — это не одна технология, а четыре разных семейства под разные задачи. Разберём каждое: как устроено и где сияет.
NoSQL (Not Only SQL) — общее название для нереляционных хранилищ, которые жертвуют частью реляционных гарантий (жёсткой схемой, сложными соединениями, иногда сильной согласованностью) ради масштабируемости, гибкости схемы или удобства под конкретную модель данных.
Зачем разбираться в семействах NoSQL
Частая ошибка — говорить «возьмём NoSQL», как будто это что-то одно. На деле под зонтиком NoSQL живут четыре принципиально разных модели данных, и выбор между ними важнее, чем выбор между конкретными продуктами. Каждое семейство решает свой класс задач, на которых реляционная модель буксует. Понимая их устройство, вы выбираете инструмент под задачу, а не под моду.
Что объединяет все NoSQL-модели
При всём разнообразии у четырёх семейств есть общие мотивы, которые стоит увидеть, прежде чем разбирать каждое. Во-первых, гибкость схемы: в отличие от реляционной модели, где структуру задают заранее и меняют с трудом, NoSQL-хранилища обычно позволяют записям различаться по набору полей и эволюционировать на лету. Во-вторых, ориентация на масштаб: большинство из них с самого начала проектировались для горизонтального распределения по многим узлам — ровно там, где реляционная модель буксует. В-третьих, специализация под модель доступа: каждое семейство оптимизировано под свой способ работы с данными (доступ по ключу, по документу, по столбцам, по связям), а не пытается быть универсальным. И в-четвёртых, как следствие, отказ от части гарантий: за гибкость и масштаб платят, как правило, ослаблением согласованности (BASE вместо ACID) и упрощением запросов (нет богатых соединений). Держа эти общие черты в голове, легче понять, почему конкретное семейство устроено именно так — оно по-своему реализует один и тот же набор приоритетов.
Хранилища ключ-значение
Самая простая модель: данные — это словарь, отображение ключ → значение. Вы кладёте значение по ключу и достаёте его по ключу; внутрь значения база обычно не заглядывает (это просто «байты»). Никаких связей, соединений или сложных запросов — только операции get/put по ключу.
За эту простоту платят отсутствием выразительных запросов, но получают феноменальную скорость и лёгкость масштабирования. Идеально для кешей, сессий пользователей, счётчиков, очередей. Примеры: Redis, Riak, Amazon DynamoDB (в простейшем режиме). Это как камера хранения: по номеру ячейки мгновенно достаёте содержимое, но искать «все чёрные чемоданы» нельзя.
Документные базы
Документная модель хранит документы — обычно JSON-подобные объекты с вложенными полями, массивами и произвольной структурой. В отличие от ключ-значение, база понимает содержимое документа: можно искать и фильтровать по внутренним полям. Схема гибкая: разные документы в одной коллекции могут иметь разный набор полей.
Сильная сторона — когда сущность естественно читается и пишется целиком (карточка товара, профиль пользователя, заказ со всеми позициями внутри). Не нужно собирать данные из пяти таблиц соединениями — весь объект лежит одним документом. Цена — контролируемая избыточность и слабость в связях «многие-ко-многим» между документами. Примеры: MongoDB, CouchDB, Amazon DocumentDB.
{
"order_id": 100,
"client": { "name": "Анна", "city": "Москва" },
"items": [
{ "product": "Книга", "qty": 2, "cena": 500 },
{ "product": "Кружка", "qty": 1, "cena": 300 }
],
"total": 1300
}
Весь заказ — один документ: клиент, позиции и итог вложены внутрь. В реляционной схеме это было бы три-четыре таблицы и соединения; здесь — одно чтение по ключу order_id.
Когда документ, а когда таблица
Поскольку документная модель — самая частая альтернатива реляционной, дадим практический ориентир выбора между ними. В сторону документа склоняют: данные естественно читаются и пишутся как цельный объект; структура у разных записей различается; связей «многие-ко-многим» мало; читают намного чаще, чем меняют общие данные; нужна гибкая, быстро эволюционирующая схема. В сторону реляционной таблицы склоняют: много сложных связей между сущностями; важна строгая целостность (финансы, учёт); часто нужны произвольные соединения и аналитические запросы; одни и те же данные используются множеством разных способов, и заранее неясно, какой «главный». Признак, что вы выбрали неверно в пользу документа: вы начинаете хранить ссылки между документами и вручную «соединять» их в коде приложения — это сигнал, что задача на самом деле реляционная. И наоборот, если в реляционной схеме вы постоянно собираете один и тот же объект из десятка таблиц на каждый запрос, возможно, его естественнее хранить документом.
Колоночные (wide-column) базы
Колоночные хранилища (точнее, «семейства столбцов», wide-column) организуют данные по строкам, но внутри строки группируют столбцы в семейства, и набор столбцов у разных строк может различаться. Физически данные часто хранятся по столбцам, что эффективно для аналитики по немногим колонкам на огромных объёмах и для разреженных данных (где у большинства строк большинство столбцов пусты).
Сильная сторона — колоссальная масштабируемость по записи и чтению немногих столбцов на петабайтах данных: логи, временные ряды, события, аналитика. Примеры: Apache Cassandra, HBase, Google Bigtable. Многие из них (Cassandra) выбирают доступность (AP по CAP) и итоговую согласованность.
Документная модель и денормализация
Документная модель ярче всего показывает, как меняется само мышление о данных при переходе от реляционного подхода. В реляционной схеме мы старательно разносили данные по таблицам, борясь с избыточностью (весь раздел про нормализацию был об этом). Документный подход часто делает обратное: сознательно собирает связанные данные в один документ, дублируя то, что нужно для быстрого чтения. Заказ хранит копию имени и города клиента прямо внутри — не нужно соединять с коллекцией клиентов. Это та самая денормализация, но возведённая в принцип, а не применённая точечно. Плата прежняя и знакомая: сменилось имя клиента — его нужно обновить во всех документах-заказах, где оно скопировано, и согласованность этих копий ложится на приложение, а не на базу. Поэтому документная модель блистает там, где данные читаются куда чаще, чем меняются, и где «единица работы» — это цельный объект (карточка, профиль, заказ). И буксует там, где много связей «многие-ко-многим» и частых изменений общих данных. Вся теория нормализации из пятого раздела здесь не отменяется — она помогает осознанно решить, что продублировать, а что вынести.
Графовые базы
Графовая модель хранит вершины (сущности) и рёбра (связи между ними), причём связи — полноправные объекты со своими свойствами. Она создана для данных, где главное — именно связи: социальные сети («друзья друзей»), рекомендации, маршруты, графы зависимостей, выявление мошеннических схем.
В реляционной модели запрос «найди друзей друзей друзей» требует многократного самосоединения и быстро становится неподъёмным. В графовой базе обход связей — естественная и быстрая операция: вы просто идёте по рёбрам. Примеры: Neo4j, Amazon Neptune, ArangoDB. Графовые базы, в отличие от многих других NoSQL, обычно делают акцент на сильной согласованности и транзакциях.
Сводная таблица
| Семейство | Единица данных | Сильно в | Примеры |
| Ключ-значение | пара ключ → значение | кеш, сессии, скорость по ключу | Redis, Riak |
| Документная | JSON-документ | самодостаточные объекты, гибкая схема | MongoDB, CouchDB |
| Колоночная | строка с семействами столбцов | огромные объёмы, аналитика, ряды | Cassandra, HBase |
| Графовая | вершины и рёбра | связи, обходы, рекомендации | Neo4j, Neptune |
Типичные заблуждения
- «NoSQL — это одна технология». Это четыре разных модели данных под разные задачи; выбор семейства важнее выбора продукта.
- «Документная база заменяет реляционную везде». Она хороша для самодостаточных объектов, но плоха для сложных связей «многие-ко-многим».
- «Все NoSQL жертвуют согласованностью». Графовые базы обычно делают упор на сильную согласованность; это зависит от семейства и продукта.
- «Ключ-значение умеет искать по содержимому». Нет: поиск только по ключу; для поиска по полям нужна документная модель.
Итог
- NoSQL — зонтик из четырёх моделей: ключ-значение, документная, колоночная, графовая.
- Ключ-значение — быстрый доступ по ключу (кеши, сессии); документная — самодостаточные JSON-объекты с гибкой схемой.
- Колоночная — масштаб и аналитика на огромных объёмах; графовая — данные, где главное связи.
- Каждое семейство решает класс задач, на которых реляционная модель буксует, — выбирают под задачу.