Skip to content

Моделирование данных NoSQL объяснённые

Паттерны моделирования для документных, ключ-значение, ширококолоночных, графовых и векторных хранилищ — когда встраивать, ссылаться, денормализовать или группировать по временным окнам.

Хранилища NoSQL меняют фиксированные схемы и join-ы на горизонтальное масштабирование. Каждое семейство моделирует связи по-своему, поэтому шаблон доступа — а не нормальная форма — диктует структуру.

Справочная таблица · 28 записи
28 of 28 rows
Документы
Вложенные поддокументы живут внутри одного родительского документа вместо отдельных таблиц.Связи один-к-немногим, читаемые вместе. Заказ со своими позициями. Пользователь с тремя адресами.
Храните внешний id, а не данные. Разрешайте его вторым чтением или $lookup.Крупные, разделяемые или независимо обновляемые сущности. Связи многие-ко-многим между коллекциями.
Родитель встраивает короткий массив детей; весь набор умещается и загружается как один документ.Пара телефонных номеров или настроек в профиле. Одно чтение возвращает родителя и детей.
Дети живут в собственной коллекции; каждый несёт id родителя, либо родитель держит ограниченный массив id.Наборы, слишком большие или слишком независимые для встраивания. Запрашивайте детей по id родителя.
Ребёнок хранит id родителя; родитель никогда не перечисляет детей, потому что набор не ограничен.События, логи или голоса, привязанные к хосту или пользователю. Фильтруйте детей по id родителя и пагинируйте.
Документы разных типов сущностей делят одну коллекцию, различаясь полем типа.Комментарии или уведомления у многих видов родителей, все запрашиваются одним способом.
Несколько экстремальных сущностей выносятся из общей формы, чтобы их размер или трафик не искажал остальные.Аккаунты знаменитостей, чьи списки подписчиков или счётчики переросли бы обычный документ.
Каждая ревизия — новый документ с тем же id и растущим номером версии; запрос выбирает последнюю.Аудиторские следы и история правок, где старые ревизии остаются запрашиваемыми.
Производные значения вычисляются при записи и хранятся рядом с данными, которые суммируют.Итоги, рейтинги и top-N, которые иначе запускали бы агрегацию при каждом чтении.
Ключ-значение и ширококолоночные
Термин проектирования DynamoDB: сворачивает много типов сущностей в одну таблицу через перегруженные ключи партиции и сортировки.Выбирайте разнородные элементы одним запросом, без join.
Один атрибут ключа служит многим типам сущностей, помечен префиксами вроде USER# или ORDER#.Дизайны single-table. Группируйте связанные элементы под одной партицией и забирайте одним запросом.
Ключ партиции распределяет строки по узлам; ключ сортировки упорядочивает каждую партицию.Cassandra и DynamoDB. Диапазонные сканы, упорядоченные логи, иерархические составные ключи.
Сложите несколько атрибутов в один ключ сортировки с разделителем — country#region#city — так, чтобы совпадение по префиксу спускалось по иерархии.Иерархии и многогранные фильтры. Один упорядоченный диапазонный запрос заменяет много поисков по индексам.
Группируйте события в фиксированные окна — час, день, месяц — под выделенными партициями или таблицами.Метрики, логи и IoT-телеметрия. Ограничивает размер партиции и состаривает холодные данные.
Дублируйте часто читаемые поля по записям, чтобы запрос никогда не гонялся за вторым чтением.Нагрузки на чтение, где итоговая согласованность допустима, а хранение дёшево.
Вторичный индекс по атрибуту, который есть лишь у некоторых элементов; элементы без него никогда не попадают в индекс.Альтернативные пути доступа. Найдите разреженное подмножество — открытые заказы, неотгруженные позиции — без полного скана.
Сначала выпишите точные запросы; раскладка таблицы выводится из них, а не из сущностей или нормальной формы.Работа со схемами в Cassandra и DynamoDB. Одна таблица на семейство запросов; модель данных копирует вопрос.
Запросу приходится параллельно бить по многим партициям или узлам, потому что ни один ключ не отвечает вопросу.Запах, который надо спроектировать прочь, а не цель. Переложите ключи так, чтобы частый запрос ложился на одну партицию.
Копии проталкиваются в представление каждого читателя при записи, либо собираются и сливаются при чтении.Фиды и ленты. Fan-out при записи для малых аудиторий, при чтении — для огромных.
Заранее построенный результат запроса, хранимый как отдельная таблица и поддерживаемый актуальным при изменении базы.Альтернативные формы чтения поверх одного пути записи. Обслуживает запросы, на которые базовая таблица ответить не может.
Графы
Каждое ребро — строка или кортеж, указывающий из исходного узла в целевой.Графовые и property-хранилища. Запросы «друзья друзей», маршрутизация, рекомендации.
Каждая связь — документ в собственной коллекции, несущий id from и to плюс атрибуты связи.Графы в документных хранилищах. Атрибуты на самой связи — рейтинг, роль, отметка времени.
Каждый узел хранит счётчики левый и правый из обхода в глубину; поддерево живёт внутри диапазона родителя.Деревья с интенсивным чтением. Один запрос BETWEEN возвращает поддерево; вставки перенумеровывают обход.
Выделенная таблица записывает каждую пару предок-потомок, включая каждый узел на нулевой глубине.Запросы целых ветвей на любой глубине. Простые join-ы, оплаченные хранением и сопровождением.
Property-графы крепят атрибуты ключ-значение к узлам и рёбрам; RDF выражает всё триплетами субъект-предикат-объект.Property-графы для богатых обходов (Neo4j); RDF для связанных открытых данных и обмена онтологиями.
Векторы
Массив float высокой размерности лежит рядом с метаданными. ANN-индекс находит ближайших соседей.Семантический поиск, извлечение RAG, рекомендации и близость над неструктурированным контентом.
Исходные документы разбиваются — по размеру, перекрытию или структуре — прежде чем каждый фрагмент получит свой эмбеддинг.Качество извлечения RAG. Размер чанка балансирует точность и полноту; перекрытие защищает разрезанные предложения.
Близость плотных векторов работает рядом с поиском по ключевым словам; два ранжированных списка сливаются в один результат.Запросы, которым сразу нужны точные термины и смысл, поверх — фильтры по метаданным.