LINUX.ORG.RU

Как обращаться с графами, хранящимися в реляционной БД?

 , ,


2

3

Привет ЛОР!

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

В данном поделии предполагается работа и визуализация графов.

В теории графов не силен, зато хорошо знаю область деятельности, для которой буду писать софтинку.

В БД так же не силен, собственно хочу изучить реляционные БД с в свободное время.

Соответственно есть вопросы к уважаемым регистрантам и анонимусам. Эти вопросы я хочу постить здесь.

Итак, первая часть вопросов

Мне нужно будет хранить направленные ациклические графы в виде узлов и связей. Я хочу делать это так, чтобы эти самые графы можно было легко копировать.

В БД, будут таблицы:

  • графы
    • id
    • desc
  • узлы
    • id
    • graph -> графы.id
    • desc
  • связи
    • id
    • src -> узлы.id
    • dst -> узлы.id

Насколько я понимаю, при копировании графа мне придется:

  • вставить новую запись в «графы»
  • запросить её id
  • вставить дубликаты узлов с новым id
  • запросить их id
  • преобразовать старые id узлов для связей в новые
  • вставить связи с новыми id узлов

Во всем это мне не нравятся два предпоследних шага. Хочется облегчить себе работу и есть две альтернативных идеи:

  • делать отдельные таблицы узлов из связей под каждый граф с ключами без автоинкремента, которые я буду генерировать сам и тупо копировать при копировании графа.

  • добавить в «связи» колонку graph -> графы.id, добавить в «узлы» колонку num (номер узла в графе) и соответственно из колонок src и dst «связей» ссылаться не на узлы.id, а на узлы.num.

На самом деле номера узлов у меня уже генерируются процедуркой, которая строит граф, так что с склоняюсь к последней идее.

Вопросы:

  • Насколько правильно я понимаю что, надо делать?
  • Как правильно обращаться с такими графами в реляционной БД?
  • Если перечисленные варианты жизнеспособны, то в каких случаях какой из них целесообразно использовать?
★★★★★

Последнее исправление: shkolnick-kun (всего исправлений: 2)

В узлах убери id, точнее оставь, но не делай его автоинкрементом, нумеруй сам. А primary ключом сделай (graph_id, id).

Из связей убери id, сделай primary ключом (graph_id, src, dest).

Тогда при копировании графа нужно будет лишь продублировать записи в этих двух таблицах меняя только graph_id.

Вообще на глаза попадались специальные БД для хранения графов вроде neo4j, может оно лучше подойдёт под задачу?

PolarFox ★★★★★
()
Последнее исправление: PolarFox (всего исправлений: 2)
Ответ на: комментарий от PolarFox

primary ключом сделай (graph_id, id)

А чо так можно было? Спасибо!

А как быть с каскадным уделением, если я граф хочу удалить?

shkolnick-kun ★★★★★
() автор топика
Последнее исправление: shkolnick-kun (всего исправлений: 2)
Ответ на: комментарий от PolarFox

Вообще на глаза попадались специальные БД для хранения графов вроде neo4j, может оно лучше подойдёт под задачу?

Прикольная штука, мне вот только интерсно: неужели графы — это такая востребованная штука, что под них одних сделали целую БД? У меня давно витает в воздухе идея создания чего-то универсального, чтобы программист сам решал, что у него там — дерево, граф, табличка, или блобы с ключами.

byko3y ★★★★
()
Ответ на: комментарий от byko3y

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

Гм.
В объектно-ориентированной СУБД все это есть.

Ныне пробую в нее добавить объекты типа *.docx и *.xlsx.
Интересно то, что объекты типа *.docx и *.xlsx Microsoft можно будет сделать на порядок более функциональными.

Но конечно - "Не все сразу!"
anonymous
()
Ответ на: комментарий от byko3y

В целом специальная бд под графы мало кому нужна, но видимо кому-то очень нужна, чтобы существовать.

PolarFox ★★★★★
()
Ответ на: комментарий от shkolnick-kun

А как быть с каскадным уделением, если я граф хочу удалить?

graph_id — foreign key в таблице узлов, в таблице связей два foreign key (graph_id, src) и (graph_id, dest). По идее всё должно нормально каскаднуться.

PolarFox ★★★★★
()
Ответ на: комментарий от byko3y

В биологии много чего описывают в виде графов и гиперграфов

Leron ★★
()
Ответ на: комментарий от shkolnick-kun

А как быть с каскадным уделением, если я граф хочу удалить?

В свойствах fogein key ставишь каскадное удаление

Psilocybe ★★★★
()
Ответ на: комментарий от byko3y

Я видел CMS, использующую neo4j. Ничего, специфичного для графов, в ней не было, просто решили сделать так. По сути любые данные можно представить в виде графов. Типа автор указывает на посты, посты указывают на дочерние посты, роли указывают на юзеров и тд.

Честно говоря не знаю, чем это лучше реляционной модели. Может масштабируется лучше?

Legioner ★★★★★
()
Ответ на: комментарий от Legioner

Честно говоря не знаю, чем это лучше реляционной модели. Может масштабируется лучше?

Я подозреваю, что это реинкарнация старинных сетевых/иерархических БД. Фишка их в том, что там был низкоуровневый указатель на кортеж данных, бегать по которому естественно быстрее, чем по ид с его поиском в индексе. Похоже на rowid в Оракле.

Побочный эффект - мозгоклюйные структуры данных, типа коралловых и т.п.

Psilocybe ★★★★
()
Ответ на: комментарий от Psilocybe

Ну в neo4j обычные запросы на своём выдуманном языке запросов, никаких там низкоуровневых указателей я не видел.

Legioner ★★★★★
()

А по теме. Советую сначала загуглить как вообще люди хранят иерахические данные в реляционной БД (это более частая проблема, чем графы), ознакомиться с подходами, там их вроде четыре штуки. И некоторые подходы там совсем нетривиальные, сам такое не выдумаешь. Подумать, что применимо к графам, подумать над плюсами-минусами каждого подхода.

Legioner ★★★★★
()
Ответ на: комментарий от byko3y

Ну да, востребованная тема. Соц. сети, реклама, аналитика, логистика(поиск путей) все это требует хранения графов. Это знаешь как спросить «а чо мобильный телефон кому-то нужен? Можно же из будки за 5 копеек позвонить»

Вопрос автору: а чем обоснован выбор реляционной бд для хранения графов? Помимо специализированных графовых бд можно же и просто в бинарных файлах хранить

cobold ★★★★★
()
Последнее исправление: cobold (всего исправлений: 1)
Ответ на: комментарий от Legioner

Честно говоря не знаю, чем это лучше реляционной модели. Может масштабируется лучше?

Речь правда о дереве /но легким движением руки …/.

В дереве нам сразу доступны подчиненные узлы /например какие-то справочники/.
В СУБД все не так …
Конечно есть «+» и «-».

До некоторой степени деревья также реляционны и много более фунциональнее …

Правда те кто не осилил

Забивают гвозди микроскопом
anonymous
()
Ответ на: комментарий от anonymous

Забивают гвозди микроскопом

Все "как обычно"

И конечно любят передать другим продвинутые методики

Забивания гвоздей микроскопом
anonymous
()
Ответ на: комментарий от anonymous

И конечно любят передать другим продвинутые методики Забивания гвоздей микроскопом

Правда и деревья не везде уместны …

anonymous
()
Ответ на: комментарий от Legioner

В теории быстрее в некоторых случаях на порядки при относительной простоте самой СУБД, если всё хорошо ложится на графы. Ещё масштабируемость при желании легко должна делаться. Ну и да, на графах есть очень-очень крутые алгоритмы.

peregrine ★★★★★
()
Последнее исправление: peregrine (всего исправлений: 1)
Ответ на: комментарий от anonymous

Sorry

Форум невезения в океане есть
Весь покрытый флудом, абсолютно весь

Там живут несчастные форумчане-дикари
На лицо ужасные, добрые внутри
На лицо ужасные, добрые внутри
Там живут несчастные форумчане-дикари

Что они ни делают, не идут дела
Видно в понедельник их мама родила
Видно в понедельник их мама родила
...

ИИ не ловится, не растёт кокос!

anonymous
()
Ответ на: комментарий от Legioner

Ну в neo4j обычные запросы на своём выдуманном языке запросов, никаких там низкоуровневых указателей я не видел.

Это обычно замаскированно под операцию «получить следующий кортеж»

Psilocybe ★★★★
()
Ответ на: комментарий от PolarFox

В узлах убери id, точнее оставь, но не делай его автоинкрементом, нумеруй сам. А primary ключом сделай (graph_id, id).

Максимально вредный совет, который можно дать. PK должен быть id с автоинкрементом в 99.99999% случаев. По разным практическим причинам.

no-such-file ★★★★★
()

В данном поделии предполагается работа и визуализация графов.

Хорошо бы на C++ …

anonymous
()
Ответ на: комментарий от anonymous

Хорошо бы на C++ …

Глупых людей не встречал /злых - мамия сколько/.
Лень - главный враг разработчика.

anonymous
()

Страница ответов и ни одного правильного. К графам нужно обращаться «Ваше сиятельство».

pinus_nigra
()

Делать графы на рбд - херовая идея. Когда будешь делать банальных обход, всё свалится жопу рекурсивных запросов. Бывают какие-то там графовые бд, но они настолько нужные, что добрая часть базоводов про них ничего не знает.

crutch_master ★★★★★
()
Ответ на: комментарий от shkolnick-kun

А как быть с каскадным уделением, если я граф хочу удалить?

Подумай лучше о том, как выбрать все связанные узлы по какому-то признаку. РБД сразу летит в помойку на такой элементарной задаче, потому что надо иметь отдельно граф в виде индекса и отдельно данные на узлах/связях.

Имею графы на рбд в продакшоке с сопутствующим гемором

crutch_master ★★★★★
()
Последнее исправление: crutch_master (всего исправлений: 3)

Если и делать велик рядом с рбд, то там никакие выборки по ключу особо не помогут, всё надо делать по пачке ид, которые будут собираться на бекенде. Сам граф связей должен как-то влазить в память. Если не собираешься крутить какими-то огромными графами, то на это можно просто забить, но если хочешь действительно базу данных, то надо думать, как он будет подгружаться при обходе, чтобы не получился износ io. Скорее всего, всё упрётся в то, что связи просто превратятся дисковые оффсеты и там уже надо будет решать всякие acid проблемы.

crutch_master ★★★★★
()
Ответ на: комментарий от Psilocybe

С чего это вдруг?

Составные типы в качестве ключа это не самая популярная фича, рано или поздно ты попадёшь в ситуацию где тебе нужно использовать сторонний код или систему где такого нет, а ты уже накатал всё на составных ключах. И будет тебе жопа.

Да и тебе самому это будет неудобно. Простой пример - как найти последнюю добавленную строку? После добавления это ещё как-то можно, но и то везде по-разному, т.е. костыли. А если не сразу, в другой части системы? Ещё большие костыли. Даже тупо сохранить полученные данные в какой-нибудь хэш-массив по ключу не везде можно, нужно будет городить свой контейнер со своим хэшем и такие затыки на каждом шагу.

no-such-file ★★★★★
()
Последнее исправление: no-such-file (всего исправлений: 1)
Ответ на: комментарий от no-such-file

Простой пример - как найти последнюю добавленную строку?

Если вдруг такое понадобится, добавляется вторая таблица для журнала действий. Обычно таких запросов не бывает.

Даже тупо сохранить полученные данные в какой-нибудь хэш-массив по ключу не везде можно

А в чём проблема? Ключ конкатенируешь с разделителем из всех полей составного. И, опять же, ещё более редкая задача.

monk ★★★★★
()
Ответ на: комментарий от no-such-file

Простой пример - как найти последнюю добавленную строку?

В рбд нет такого понятия, как «последняя строка». Эта самая «последняя» может быть добавлена когда угодно, например, в рамках каких-нибудь тех. работ.

crutch_master ★★★★★
()
Ответ на: комментарий от monk

добавляется вторая таблица для журнала действий

А в ней таки последовательный PK. Больше костылей богу костылей.

Ключ конкатенируешь с разделителем из всех полей составного

Зачем это делать, если можно не делать? Конкретно ТС-у зачем нужен такой PK вместо того, чтобы иметь просто индекс и внешние ключи по (graph_id, node_id)?

no-such-file ★★★★★
()
Ответ на: комментарий от crutch_master

Эта самая «последняя» может быть добавлена когда угодно, например, в рамках каких-нибудь тех. работ

И что? Техработы как бы нужны. Я как раз о том и говорю, что на практике строки должны быть нумерованы. По всяким техническим причинам в т.ч.

no-such-file ★★★★★
()
Ответ на: комментарий от no-such-file

А в ней таки последовательный PK. Больше костылей богу костылей.

Не костыли, а принцип единой ответственности. Потом ты захочешь знать когда эта запись была добавлена и сделаешь PK от времени. Потом захочешь знать кто добавил и добавишь поле автор. Потом захочешь узнать когда последний раз её меняли, всё выкинешь и придёшь к моему варианту.

Зачем это делать, если можно не делать?

Аналогичны вопрос к утверждению, что имея уникальность по сложному ключу нужно добавлять суррогатный последовательный. Замечу, что конкатенация для хэша хотя бы место и время при нормальной работе с БД не добавляет.

monk ★★★★★
()
Ответ на: комментарий от no-such-file

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

monk ★★★★★
()
Ответ на: комментарий от monk

Потом ты захочешь знать когда эта запись была добавлена и сделаешь PK от времени

Потом захочешь узнать когда последний раз её меняли

Для этого обычно делают created_at, updated_at, а не парят мозг отдельным логом. Лог никогда не поздно добавить, если он вдруг понадобится.

Аналогичны вопрос к утверждению, что имея уникальность по сложному ключу нужно добавлять суррогатный последовательный

Не всегда это возможно для не PK.

конкатенация для хэша хотя бы место и время при нормальной работе с БД не добавляет

Не совсем понял, как бы в приложении тебе нужно будет тратить время на эту конкатенацию, а иногда и сортировку элементов ключа (чтобы они были всегда в одном порядке), и также хранение этих сериализованных дубликатов. В самой БД тоже могут быть варианты, смотря как там реализовано хранение. А то ведь может быть тоже какая-то дополнительная сериализация туда-сюда. Это всё и есть гемор, об этом нужно беспокоиться, заранее проверять, привязываться к конкретным реализациям и т.д.

no-such-file ★★★★★
()
Ответ на: комментарий от no-such-file

Я как раз о том и говорю, что на практике строки должны быть нумерованы.

Если это где-то нужно, их порядок важен и вообще за него будет кто-то нести ответственность.

crutch_master ★★★★★
()
Ответ на: комментарий от monk

Потому что может быть добавлена с явным указанием PK вместо удалённой старой

Ну это ССЗБ. Так не надо делать, как и составные PK. По крайней мере ты признал проблему.

no-such-file ★★★★★
()
Ответ на: комментарий от no-such-file

По крайней мере ты признал проблему.

Проблема, которую ты описал, решается корректно исключительно дополнительной таблицей, а не нумерацией строк в каждой существующей таблице.

Кроме того, нумерация строк крайне затрудняет масштабирование базы на несколько узлов.

monk ★★★★★
()
Последнее исправление: monk (всего исправлений: 1)
Ответ на: комментарий от no-such-file

Лог никогда не поздно добавить, если он вдруг понадобится.

Вот именно. Поэтому иметь дополнительное поле для PK не надо.

как бы в приложении тебе нужно будет тратить время на эту конкатенацию

Только в том случае, если нужен идентификатор. Кроме как для хеша он не нужен. Хеш по идентификатору в БД нужен редко (чаще проще или в SQL запрос делать с нужным результатом или сразу всё затягивать с идентификатором — адресом в памяти).

сортировку элементов ключа (чтобы они были всегда в одном порядке)

Если одно поле, всё равно надо сортировать.

В самой БД тоже могут быть варианты, смотря как там реализовано хранение.

Первичный ключ на несколько полей алгоритмически ничем не отличается от ключа на одно поле.

monk ★★★★★
()
Ответ на: комментарий от monk

алгоритмически

На практике везде есть нюансы.

Если одно поле, всё равно надо сортировать

Что сортировать? Там один элемент.

решается корректно

define корректно. Разворачивать хадуп для сложения 2+2 тоже «корректное, масштабируемое решение».

нумерация строк крайне затрудняет масштабирование базы на несколько узлов

ЛОЛ, а ручная генерация graph_id, node_id конечно не затрудняет АГА. Да ТС на одном узле забибикается локи лепить чтобы разные люди одинаковые id не получили.

На нескольких узлах для записи проблемы с транзакциями в принципе, там вообще архитектурно другие подходы нужны.

no-such-file ★★★★★
()
Ответ на: комментарий от no-such-file

Что сортировать? Там один элемент.

Значения. Или что ты хочешь сортировать в составном ключе? Порядок полей в нём? Так он в описании ключа указан.

define корректно.

Алгоритм, всегда возвращающий верный ответ. В данном случае для задачи «получить последнюю добавленную строку».

Да ТС на одном узле забибикается локи лепить чтобы разные люди одинаковые id не получили.

Зачем? На узле CREATE SEQUENCE. graph_id строится из префикса узла и значения последовательности. Гарантирована уникальность по кластеру.

monk ★★★★★
()
Ответ на: комментарий от crutch_master

Не буду я делать обход через БД. На таблицы все отлично ложится. Граф - это одно из представлений данных.

shkolnick-kun ★★★★★
() автор топика
Ответ на: комментарий от crutch_master

До 10 тыс «узлов» на локалхосте же!

И то, в силу специфики задачи, человеки, весма вероятно, не осилят запилить даже 1000 узлов.

shkolnick-kun ★★★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.