Здравствуйте, меня зовут Дмитрий Карловский и я… антиконформист, то есть человек, который не держится за свои привычки и всегда готов их поменять, если в том есть необходимость. Например, как и многие разработчики, я начинал изучение баз данных с реляционных. Хотя реляционная алгебра и довольно красива в своей простоте, я постоянно ловил себя на мысли, что пытаюсь впихнуть круглую фигуру в квадратное отверстие и получалось как-то не герметично.
Нет, я не буду рассказывать вам про MongoDB или ещё какую неполноценную «убийцу SQL». Статей на тему «SQL vs NoSQL» сравнивающих на самом деле реляционные субд с документными и так полно:
Но у большинства из них есть фатальный недостаток — авторы просто не в курсе, что моделей данных в СУБД есть куда больше, чем два упомянутых: от узкоспециализированных «словарей», то универсальных «графов». А популярные «реляционные» и «документные» находятся лишь где-то по середине между универсальностью и специализированностью.
Давайте сравним типичных представителей упомянутых типов СУБД (от большего к меньшему).
Силы тысяч разработчиков направлены на то, чтобы довольно простые модели предметной области расположить в табличках так, чтобы это было быстро, гибко и не слишком сложно. Получается плохо. Пишутся огромные ORM фреймворки, зубодробительные SQL запросы, создаются огромные индексы, и дублируются данные.
В качестве примера приведу одну из типичных задач — работа с деревьями. Это может быть дерево комментариев, дерево разделов сайта, дерево тэгов. Деревьев очень много в предметных областях, но работая с реляционными СУБД их стараются избегать, так как работа с ними вызывает много боли.
Вот, сколько статей на одном только Хабре написано о проблеме, которая есть исключительно в РСУБД ввиду попытки упихнуть всё многообразие моделей предметной области в прямоугольные таблицы:
Все решения сводятся к трём основным:
Таблица смежности. Потомок хранит ссылку на родителя. Тут не сохраняется порядок потомков (чтобы его сохранять нужно вводить дополнительную нумерацию, по которой сортировать, что замедляет как вставки так и выборки). Требуются рекурсивные запросы или денормализация таблиц смежностей.
Рекурсивный запрос поддерева:
Запрос поддерева по денормализованной таблице смежности:
Материализованный путь. Потомок хранит номера всех предков (с сохранением их порядка). В худшем случае изменение иерархии приводит к обновлению данных всех узлов. При отсутствии соответствующего АПИ со стороны СУБД, требует фигурной манипуляции над строками, что не во всех случаях приемлемо по скорости. Не допускает вхождение одного потомка в несколько предков.
Запрос поддерева с использованием ordpath:
Вложенные интервалы. Каждый узел хранит два числа, которые определяют его точное положение в иерархии. В худшем случае изменение иерархии приводит к обновлению данных всех узлов. Не допускает вхождение одного потомка в несколько предков. Относительно сложные алгоритмы требуют повышенной аккуратности.
Запрос поддерева:
Изменение деревьев гораздо сложнее, примеры кода можно найти по ссылкам выше.
Как видим, каждый тип решения имеет свои серьёзные ограничения на представимую им модель и эффективность разных типов запросов. А знаете почему нет такого большого числа обстоятельных статей про хранение деревьев, например, в графовых СУБД? Да потому, что там в принципе нет этих проблем, так как дерево — это частный случай графа. Так что вопрос «как же мне так исхитриться и сохранить в базу иерархию» в графовых базах вообще не стоит.
Запрос поддерева в графе:
Да, нереляционные СУБД, не смотря на общее название «NoSQL», тоже могут поддерживать Structured Query Language, расширяя его своими операторами :-)
Многие SQL-профи тут обычно заявляют, мол деревья и тем более графы в предметных областях почти не встречаются. Но стоит немного выйти из зоны комфорта как тут же увидишь, что любая предметная область на самом деле представляет из себя граф — набор сущностей, между которыми есть разнообразные отношения. Если отношения эти 1-к-1 или хотя бы 1-ко-многим и при этом связывают лишь разнотипные сущности, то такие модели относительно легко ложатся на реляционную модель (если не учитывать разные виды джойнов с костылями в виде индексов). Но обычно всё не так. Во многих местах можно встретить отношения многие-ко-многим. В РСУБД для каждого такого отношения приходится заводить отдельную таблицу и несколько индексов к ней, а это усложнение архитектуры, раздувание данных и замедление работы с ними.
Некоторые, особо «передовые» программисты, предлагают хранить каждый тип моделей в своей СУБД. «Важные данные» в реляционных, деревья в графовых, а примитивные вообще в словарях. Но подобные подходы вида «всякой задаче свой инструмент» лишь добавляют головной боли (и как следствие багов разной степени тяжести) на тему консистентности данных в разных частях приложения.
Программная разработка — штука очень динамичная. Сегодня мастер у вас должен иметь одну профессию и вы просто даёте ему текстовое поле вписать её. Завтра потребуется, чтобы он мог указать профессию лишь из предложенных вами и вы даёте ему селект для выбора нужной, сохраняя id профессии в модель мастера (один-ко-многим). После завтра потребуется, чтобы он мог выбрать несколько профессий разом (многие-ко-многим). А через неделю вам срочно потребуется реализовать уже иерархический каталог профессий. В реляционной СУБД сложность каждого следующего перехода значительно превосходит предыдущий. С графовой — вы больше времени потратите на обсуждение, чем на реализацию. Так что при старте проекта имеет смысл брать наиболее гибкий инструмент, который позволит вам не терять темп разработки в процессе изменения бизнес требований (или лучшего понимания оных). Да, специализированные инструменты могут в некоторых случаях быть быстрее и именно в этих случаях, если в этом есть необходимость, стоит заниматься такого рода оптимизацией.
Часто толковые SQL-разработчики берут какую-нибудь MongoDB, о которой говорят на каждом углу, и пытаются примерить к своему проекту, но разобравшись с ней, недоумевающе крутят пальцем у виска. Бестолковые так и остаются на MongoDB, мирясь с отсутствием транзакций и отношений между документами, ради мифической скорости и возможности засунуть в документ любой json.
Нет, MongoDB даже среди документных СУБД — так себе, а уж в качестве альтернативы полноценным реляционным её в принципе нельзя рассматривать. А вот другая документная СУБД — OrientDB, бьёт реляционные по всем фронтам. Да, OrientDB на самом деле документная СУБД, которая, благодаря прямым ссылкам между документами позволяет описывать произвольные графы.
Давайте развеем несколько типичных мифов по поводу NoSQL, о котором судят по наиболее громким представителям — MongoDB и Redis:
1. Они не контролируют структуру записываемых пользователем данных. У отсутствия схем есть и плюсы (можно делать миграции данных в фоне, можно хранить не только кортежи примитивов), но и минусы (нужно внимательно следить что записываешь в базу, не эффективно хранятся данные). В OrientDB нашли разумный компромисс: вы можете указать схему и указанные там поля будут валидироваться в соответствии с ней при записи и не будут тратить место на имена полей, а не указанные валидироваться не будут, но будут занимать чуть больше места. Тут важно отметить, что изменение схемы не приводит к изменению самих документов — просто вы не сможете изменить их пока не приведёте к новому формату.
2. Они не удовлетворяют требованиям ACID (Атомарность, Согласованность, Изолированность, Надёжность). OrientDB этим требованиям удовлетворяет. Более того, она из коробки умеет партицирование и мастер-мастер репликацию, так что поддерживает в том числе и распределённые транзакции. При этом вы можете регулировать баланс между согласованностью и скоростью ответа:
writeQuorum определяет число узлов, которые должны подтвердить запись, прежде чем СУБД вернёт ответ, об успешном завершении транзакции.
readQuorum определяет число узлов, которые должны подтвердить актуальность данных, прежде чем данные вернутся в ответ на запрос.
По умолчанию все транзакции синхронны (дожидаемся ответа всех реплик), а чтение соответственно происходит без подтверждения актуальности (за её не надобностью в этом случае).
Примечательной особенностью является прозрачная поддержка map-reduce: если узел содержит не все данные, то он сам делает запрос к остальным узлам и сливает их ответы. Клиент может работать с партицированной базой данных как с цельной. Есть даже автобалансировщик, раскидывающий документы в кластеры по разным стратегиям.
3. Они не поддерживают SQL и предоставляют лишь простой специализированный API. OrientDB написана на Java и может быть интегрирована в другое Java приложение в качестве библиотеки. При этом есть несколько уровней публичного API:
Объектное. Низкоуровневый доступ к записям. Не рекомендуется к использованию, так как через него вы можете нарушить совместимость с двумя остальными API.
Документное. Тут вы имеете весь основной функционал основанный на документах и перекрёстных ссылках.
Графовое. Оно предоставляет дополнительные удобства для организации графов, но на практике всё же удобней оказалось использовать документное апи.
В качестве фронтенда к этим Java-API есть несколько библиотек, позволяющих писать запросы на разных языках (SQL, Gremlin, SPARQL).
Можно расширять функционал плагинами на Java. Собственно Lucene индекс и админка реализованы в качестве плагинов.
Так что не все NoSQL одинаково бесполезны :-)
Для сравнения подходов работы с графовой и реляционной СУБД, давайте создадим простейшую бизнес сущность — персону:
SQL
OSQL
Тут всё просто и почти одинаково. Теперь добавим отношение «друзья»:
SQL
OSQL
Недостатки РСУБД уже начинают вылезать — нам потребовалась дополнительная таблица и уникальный индекс на ней. Индекс этот с одной стороны гарантирует, что у нас не будет дубликатов связей, а с другой позволяет быстро находить друзей по идентификатору пользователя.
Выведем основные данные о друзьях одного из пользователей:
SQL
OSQL
В РСУБД мы не можем хранить ссылки между записями — только их идентификаторы. Поэтому появляется хитрая конструкция, объясняющая как данные из одной таблицы соотносятся с другой. В графовой же мы просто разворачиваем ссылки.
А теперь найдём друзей его друзей:
SQL
OSQL
В РСУБД запрос заметно усложнился. По хорошему его нужно усложнить ещё сильнее, чтобы данные друзей первого уровня не дублировались для каждого друга второго уровня.
Продолжать можно долго, но суть, я думаю, уже ясна. Немного больше подробностей о различиях реляционного подхода и графового можно почерпнуть из презентации "How Graph Databases started the Multi Model revolution".
На последок отмечу, что в реляционных базах данные хранятся таблицами, но они крайне не эффективны при запросах, поэтому к ним добавляют автогенерируемое дерево — индекс. Причём индексы стараются делать покрывающими, то есть не требующими обращения к собственно таблицам за данными. Так вот, если вырезать таблицы и позволить индексному дереву иметь циклы, то мы получим ни что иное как графовую СУБД, где вся база данных — это один большой максимально эффективный индекс.
Последний год я занимался разработкой бэкенда для проекта SKEDDY (Поиск мастеров и запись к ним на услуги). Звучит вроде бы не сложно, однако число бизнес сущностей сейчас уже равно 20 (20 таблиц сущностей, если бы я использовал РСУБД): person, mail, phone, social, token, application, profession, service, meeting, assessment, album, image, notification, place, track, payment, article, aspect, facet, salon.
Между ними порядка 50 отношений, из них около 20 являются многими-ко-многим (ещё 20 таблиц смежностей и 20-40 индексов в РСУБД). В OrientDB же потребовался лишь один индекс для трансляции внешних айдишников во внутренние (внешние — человекопонятные и изменяемые, внутренние — персистентные и «странные»), один для фасетного поиска услуг плюс ещё пяток полнотекстовых и всё. Граф полностью нормализован, большинство запросов идут по прямым ссылкам между узлами, что позволяет делать глубокие выборки, не теряя в производительности и наглядности запросов.
Благодаря графовой модели данных, отображение на неё бизнес модели происходит легко и просто — единожды написанный простой обобщённый код для синхронизации с базой данных, позволил программировать в терминах бизнес сущностей без лишних забот об оптимизации запросов к СУБД. Собственно борьба с AngularJS отняла куда больше времени, но это уже совсем другая история…
Теперь о недостатках:
Документация довольно не плоха для основного функционала, но некоторые аспекты в ней отражены крайне слабо. Например, информацию о хуках приходится собирать по крупицам.
Больше что-то на ум ничего не приходит :-)
На этом всё. Кто заинтересовался, но что-то недопонял — не стесняйтесь задавать вопросы в комментариях.
Нет, я не буду рассказывать вам про MongoDB или ещё какую неполноценную «убийцу SQL». Статей на тему «SQL vs NoSQL» сравнивающих на самом деле реляционные субд с документными и так полно:
- MongoDB или как разлюбить SQL
- Реляционные базы данных обречены?
- NoSQL базы данных: понимаем суть
- Почему нужно 1000 раз подумать, прежде чем использовать noSQL
- SQL — гибок или почему я боюсь NoSQL
- NoSQL и Big Data – обман трудящихся?
- Чем поможет архитектору «NoSQL» и… поможет ли?
- Сравнение производительности MongoDB vs PostgreSQL. Часть I: No index
- Сравнение производительности MongoDB vs PostgreSQL. Часть II: Index
- Разбираем ACID по буквам в NoSQL
- Почему вы никогда не должны использовать MongoDB
- Почему мы выбрали MongoDB
- Недостатки RDBMS или RDBMS vs NoSQL
- Почему вы никогда не должны говорить «никогда»
- Еж с ужом в одной корзине, а также немного об отсутствии схемы
- Почему реляционные СУБД отлично подходят для стартапов: Пример из истории разработки мессенджера Kato
- Прощай, MongoDB, здравствуй, PostgreSQL
Но у большинства из них есть фатальный недостаток — авторы просто не в курсе, что моделей данных в СУБД есть куда больше, чем два упомянутых: от узкоспециализированных «словарей», то универсальных «графов». А популярные «реляционные» и «документные» находятся лишь где-то по середине между универсальностью и специализированностью.
Давайте сравним типичных представителей упомянутых типов СУБД (от большего к меньшему).
- Популярность: Oracle, MongoDB, Redis, HBase, OrientDB.
- Функциональность: OrientDB, Oracle, MongoDB, HBase, Redis.
- Скорость: очень сильно зависит от задачи, данных и реализации приложения. Я пересмотрел кучу бенчмарков, везде всё по разному.
SQL
Силы тысяч разработчиков направлены на то, чтобы довольно простые модели предметной области расположить в табличках так, чтобы это было быстро, гибко и не слишком сложно. Получается плохо. Пишутся огромные ORM фреймворки, зубодробительные SQL запросы, создаются огромные индексы, и дублируются данные.
В качестве примера приведу одну из типичных задач — работа с деревьями. Это может быть дерево комментариев, дерево разделов сайта, дерево тэгов. Деревьев очень много в предметных областях, но работая с реляционными СУБД их стараются избегать, так как работа с ними вызывает много боли.
Вот, сколько статей на одном только Хабре написано о проблеме, которая есть исключительно в РСУБД ввиду попытки упихнуть всё многообразие моделей предметной области в прямоугольные таблицы:
- Решение проблемы сортировки деревьев с помощью бинарного Materialized Path
- MS SQL: hierarchyid — иерархия по-новому
- Иерархические (рекурсивные) запросы
- Иерархические структуры данных и Doctrine
- Иерархические структуры данных и производительность
- Nested Sets + PostgreSQL TRIGGER
- Nested Sets + MySQL TRIGGER
- Full Hierarchy — иерархические структуры в базах данных
- Задача отображения деревьев в MySql. Способ отображения на хранимых процедурах
- Рекурсивные (Иерархические) запросы в PostgreSQL
- Реализация иерархии — объединение Adjacency List и Materialized Path через one-to-many
- Строим Nested Set дерево без рекурсии
- Степени — ключ к быстрой иерархии (пример на Django)
- Иерархические данные. В поиске оптимального решения
- Хранение деревьев в базе данных. Часть первая, теоретическая
- Денормализация деревьев
- Где смерть Кащеева?
- Nested Intervals и их реализация под Yii2
- Хранение иерархических структур. Симбиоз «Closure Table» и «Adjacency List»
- Cartesius — метод хранения и извлечения древовидных структур в реляционных базах данных или SQL деревья без червей и тараканов
Все решения сводятся к трём основным:
Таблица смежности. Потомок хранит ссылку на родителя. Тут не сохраняется порядок потомков (чтобы его сохранять нужно вводить дополнительную нумерацию, по которой сортировать, что замедляет как вставки так и выборки). Требуются рекурсивные запросы или денормализация таблиц смежностей.
Рекурсивный запрос поддерева:
WITH RECURSIVE Rec(id, parent, name, ord)
AS (
SELECT id, parent, name, ord FROM tree
UNION ALL
SELECT Rec.id, Rec.parent, Rec.name, Rec.ord
FROM Rec, tree
WHERE Rec.id = tree.parent
)
SELECT * FROM Rec
WHERE parent = 123
ORDER BY ord
Запрос поддерева по денормализованной таблице смежности:
SELECT navi.id , navi.name , navi.parent
FROM tree , navi
WHERE tree.ancestor = 123 AND navi.id = tree.node
ORDER BY ord
Материализованный путь. Потомок хранит номера всех предков (с сохранением их порядка). В худшем случае изменение иерархии приводит к обновлению данных всех узлов. При отсутствии соответствующего АПИ со стороны СУБД, требует фигурной манипуляции над строками, что не во всех случаях приемлемо по скорости. Не допускает вхождение одного потомка в несколько предков.
Запрос поддерева с использованием ordpath:
SELECT RowId, name FROM dbo.Tree WHERE @ParentId.IsDescendant(RowId) = 123
Вложенные интервалы. Каждый узел хранит два числа, которые определяют его точное положение в иерархии. В худшем случае изменение иерархии приводит к обновлению данных всех узлов. Не допускает вхождение одного потомка в несколько предков. Относительно сложные алгоритмы требуют повышенной аккуратности.
Запрос поддерева:
SELECT node.id, node.name
FROM tree AS node, tree AS parent
WHERE node.left BETWEEN parent.left AND parent.right
AND parent.id = 123
ORDER BY node.left;
Изменение деревьев гораздо сложнее, примеры кода можно найти по ссылкам выше.
Как видим, каждый тип решения имеет свои серьёзные ограничения на представимую им модель и эффективность разных типов запросов. А знаете почему нет такого большого числа обстоятельных статей про хранение деревьев, например, в графовых СУБД? Да потому, что там в принципе нет этих проблем, так как дерево — это частный случай графа. Так что вопрос «как же мне так исхитриться и сохранить в базу иерархию» в графовых базах вообще не стоит.
Запрос поддерева в графе:
SELECT name , parent FROM ( TRAVERSE child FROM #1:123 )
Да, нереляционные СУБД, не смотря на общее название «NoSQL», тоже могут поддерживать Structured Query Language, расширяя его своими операторами :-)
Многие SQL-профи тут обычно заявляют, мол деревья и тем более графы в предметных областях почти не встречаются. Но стоит немного выйти из зоны комфорта как тут же увидишь, что любая предметная область на самом деле представляет из себя граф — набор сущностей, между которыми есть разнообразные отношения. Если отношения эти 1-к-1 или хотя бы 1-ко-многим и при этом связывают лишь разнотипные сущности, то такие модели относительно легко ложатся на реляционную модель (если не учитывать разные виды джойнов с костылями в виде индексов). Но обычно всё не так. Во многих местах можно встретить отношения многие-ко-многим. В РСУБД для каждого такого отношения приходится заводить отдельную таблицу и несколько индексов к ней, а это усложнение архитектуры, раздувание данных и замедление работы с ними.
Некоторые, особо «передовые» программисты, предлагают хранить каждый тип моделей в своей СУБД. «Важные данные» в реляционных, деревья в графовых, а примитивные вообще в словарях. Но подобные подходы вида «всякой задаче свой инструмент» лишь добавляют головной боли (и как следствие багов разной степени тяжести) на тему консистентности данных в разных частях приложения.
Программная разработка — штука очень динамичная. Сегодня мастер у вас должен иметь одну профессию и вы просто даёте ему текстовое поле вписать её. Завтра потребуется, чтобы он мог указать профессию лишь из предложенных вами и вы даёте ему селект для выбора нужной, сохраняя id профессии в модель мастера (один-ко-многим). После завтра потребуется, чтобы он мог выбрать несколько профессий разом (многие-ко-многим). А через неделю вам срочно потребуется реализовать уже иерархический каталог профессий. В реляционной СУБД сложность каждого следующего перехода значительно превосходит предыдущий. С графовой — вы больше времени потратите на обсуждение, чем на реализацию. Так что при старте проекта имеет смысл брать наиболее гибкий инструмент, который позволит вам не терять темп разработки в процессе изменения бизнес требований (или лучшего понимания оных). Да, специализированные инструменты могут в некоторых случаях быть быстрее и именно в этих случаях, если в этом есть необходимость, стоит заниматься такого рода оптимизацией.
NoSQL
Часто толковые SQL-разработчики берут какую-нибудь MongoDB, о которой говорят на каждом углу, и пытаются примерить к своему проекту, но разобравшись с ней, недоумевающе крутят пальцем у виска. Бестолковые так и остаются на MongoDB, мирясь с отсутствием транзакций и отношений между документами, ради мифической скорости и возможности засунуть в документ любой json.
Нет, MongoDB даже среди документных СУБД — так себе, а уж в качестве альтернативы полноценным реляционным её в принципе нельзя рассматривать. А вот другая документная СУБД — OrientDB, бьёт реляционные по всем фронтам. Да, OrientDB на самом деле документная СУБД, которая, благодаря прямым ссылкам между документами позволяет описывать произвольные графы.
Давайте развеем несколько типичных мифов по поводу NoSQL, о котором судят по наиболее громким представителям — MongoDB и Redis:
1. Они не контролируют структуру записываемых пользователем данных. У отсутствия схем есть и плюсы (можно делать миграции данных в фоне, можно хранить не только кортежи примитивов), но и минусы (нужно внимательно следить что записываешь в базу, не эффективно хранятся данные). В OrientDB нашли разумный компромисс: вы можете указать схему и указанные там поля будут валидироваться в соответствии с ней при записи и не будут тратить место на имена полей, а не указанные валидироваться не будут, но будут занимать чуть больше места. Тут важно отметить, что изменение схемы не приводит к изменению самих документов — просто вы не сможете изменить их пока не приведёте к новому формату.
2. Они не удовлетворяют требованиям ACID (Атомарность, Согласованность, Изолированность, Надёжность). OrientDB этим требованиям удовлетворяет. Более того, она из коробки умеет партицирование и мастер-мастер репликацию, так что поддерживает в том числе и распределённые транзакции. При этом вы можете регулировать баланс между согласованностью и скоростью ответа:
writeQuorum определяет число узлов, которые должны подтвердить запись, прежде чем СУБД вернёт ответ, об успешном завершении транзакции.
readQuorum определяет число узлов, которые должны подтвердить актуальность данных, прежде чем данные вернутся в ответ на запрос.
По умолчанию все транзакции синхронны (дожидаемся ответа всех реплик), а чтение соответственно происходит без подтверждения актуальности (за её не надобностью в этом случае).
Примечательной особенностью является прозрачная поддержка map-reduce: если узел содержит не все данные, то он сам делает запрос к остальным узлам и сливает их ответы. Клиент может работать с партицированной базой данных как с цельной. Есть даже автобалансировщик, раскидывающий документы в кластеры по разным стратегиям.
3. Они не поддерживают SQL и предоставляют лишь простой специализированный API. OrientDB написана на Java и может быть интегрирована в другое Java приложение в качестве библиотеки. При этом есть несколько уровней публичного API:
Объектное. Низкоуровневый доступ к записям. Не рекомендуется к использованию, так как через него вы можете нарушить совместимость с двумя остальными API.
Документное. Тут вы имеете весь основной функционал основанный на документах и перекрёстных ссылках.
Графовое. Оно предоставляет дополнительные удобства для организации графов, но на практике всё же удобней оказалось использовать документное апи.
В качестве фронтенда к этим Java-API есть несколько библиотек, позволяющих писать запросы на разных языках (SQL, Gremlin, SPARQL).
Можно расширять функционал плагинами на Java. Собственно Lucene индекс и админка реализованы в качестве плагинов.
Так что не все NoSQL одинаково бесполезны :-)
NewSQL
Для сравнения подходов работы с графовой и реляционной СУБД, давайте создадим простейшую бизнес сущность — персону:
SQL
create table Persons (
name text,
age smallint
)
OSQL
create class Person
create property Person.name string
create property Person.age short
Тут всё просто и почти одинаково. Теперь добавим отношение «друзья»:
SQL
create table Persons_friends (
subject integer,
object integer
)
create unique index Persons_friends on Persons_friends ( subject , object )
OSQL
create property Person.friend linkset Person
Недостатки РСУБД уже начинают вылезать — нам потребовалась дополнительная таблица и уникальный индекс на ней. Индекс этот с одной стороны гарантирует, что у нас не будет дубликатов связей, а с другой позволяет быстро находить друзей по идентификатору пользователя.
Выведем основные данные о друзьях одного из пользователей:
SQL
select
Persons.rowid ,
Persons.name ,
Persons.age
from
Persons_friends as friends,
Persons
where
friends.subject = 123 ,
friends.object = Persons.rowid
OSQL
select expand( friend )
from #19:0
fetchplan *:-2 name:0 age:0
В РСУБД мы не можем хранить ссылки между записями — только их идентификаторы. Поэтому появляется хитрая конструкция, объясняющая как данные из одной таблицы соотносятся с другой. В графовой же мы просто разворачиваем ссылки.
А теперь найдём друзей его друзей:
SQL
select
Persons_1.rowid ,
Persons_1.name ,
Persons_1.age ,
Persons_2.rowid ,
Persons_2.name ,
Persons_2.age
from
Persons_friends as friends_1 ,
Persons_friends as friends_2 ,
Persons as Persons_1 ,
Persons as Persons_2
where
friends_1.subject = 123 ,
friends_1.object = Persons_1.rowid ,
friends_1.object = friends_2.subject ,
friends_2.object = Persons_2.rowid
OSQL
select expand( friend )
from #19:0
fetchplan *:-2
name:0
age:0
friend.name:0
friend.age:0
В РСУБД запрос заметно усложнился. По хорошему его нужно усложнить ещё сильнее, чтобы данные друзей первого уровня не дублировались для каждого друга второго уровня.
Продолжать можно долго, но суть, я думаю, уже ясна. Немного больше подробностей о различиях реляционного подхода и графового можно почерпнуть из презентации "How Graph Databases started the Multi Model revolution".
На последок отмечу, что в реляционных базах данные хранятся таблицами, но они крайне не эффективны при запросах, поэтому к ним добавляют автогенерируемое дерево — индекс. Причём индексы стараются делать покрывающими, то есть не требующими обращения к собственно таблицам за данными. Так вот, если вырезать таблицы и позволить индексному дереву иметь циклы, то мы получим ни что иное как графовую СУБД, где вся база данных — это один большой максимально эффективный индекс.
Хватит теории. Что на практике-то?
Последний год я занимался разработкой бэкенда для проекта SKEDDY (Поиск мастеров и запись к ним на услуги). Звучит вроде бы не сложно, однако число бизнес сущностей сейчас уже равно 20 (20 таблиц сущностей, если бы я использовал РСУБД): person, mail, phone, social, token, application, profession, service, meeting, assessment, album, image, notification, place, track, payment, article, aspect, facet, salon.
Между ними порядка 50 отношений, из них около 20 являются многими-ко-многим (ещё 20 таблиц смежностей и 20-40 индексов в РСУБД). В OrientDB же потребовался лишь один индекс для трансляции внешних айдишников во внутренние (внешние — человекопонятные и изменяемые, внутренние — персистентные и «странные»), один для фасетного поиска услуг плюс ещё пяток полнотекстовых и всё. Граф полностью нормализован, большинство запросов идут по прямым ссылкам между узлами, что позволяет делать глубокие выборки, не теряя в производительности и наглядности запросов.
Благодаря графовой модели данных, отображение на неё бизнес модели происходит легко и просто — единожды написанный простой обобщённый код для синхронизации с базой данных, позволил программировать в терминах бизнес сущностей без лишних забот об оптимизации запросов к СУБД. Собственно борьба с AngularJS отняла куда больше времени, но это уже совсем другая история…
Теперь о недостатках:
Документация довольно не плоха для основного функционала, но некоторые аспекты в ней отражены крайне слабо. Например, информацию о хуках приходится собирать по крупицам.
Больше что-то на ум ничего не приходит :-)
На этом всё. Кто заинтересовался, но что-то недопонял — не стесняйтесь задавать вопросы в комментариях.
Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Какой тип СУБД вы предпочитаете?
29.29% Словарную (key-value)111
2.37% Триплетную (rdf)9
26.91% Документную102
68.87% Табличную261
5.8% Колоночную22
3.43% Древовидную (xml)13
15.3% Графовую58
5.01% Иммутабельную (только пополняемую патчами)19
Проголосовали 379 пользователей. Воздержались 398 пользователей.