Comments

Что только люди не придумают, лишь бы не изучать работу с графами :-) Смотрите, как это делается с графовой субд:
Есть документы — это узлы графа (строчки в терминах рсубд, но иерархические и с перекрёстными ссылками).
Есть классы документов — к ним привязываются схемы, триггеры и пр (таблицы в терминал рсубд).
Есть кластеры — это группы любых узлов, хранящиеся вместе (ленты в ваших терминах).
Есть ноды — физические машинки.


Теперь следите за руками:


CREATE CLASS twomass_psc;

CREATE PROPERTY twomass_psc.ra DOUBLE;
CREATE PROPERTY twomass_psc.decl DOUBLE;
CREATE PROPERTY twomass_psc.xyz LINK twomass_xyz;
CREATE PROPERTY twomass_psc.coadd_key INTEGER;
CREATE PROPERTY twomass_psc.coadd SHORT;

CREATE CLASS twomass_xyz;

CREATE PROPERTY twomass_xyz.j_m FLOAT;
CREATE PROPERTY twomass_xyz.j_cmsig FLOAT;
CREATE PROPERTY twomass_xyz.j_msigcom FLOAT;
CREATE PROPERTY twomass_xyz.j_snr FLOAT;
CREATE PROPERTY twomass_xyz.h_m FLOAT;
CREATE PROPERTY twomass_xyz.h_cmsig FLOAT;
CREATE PROPERTY twomass_xyz.h_msigcom FLOAT;
CREATE PROPERTY twomass_xyz.h_snr FLOAT;
CREATE PROPERTY twomass_xyz.k_m FLOAT;
CREATE PROPERTY twomass_xyz.k_cmsig FLOAT;
CREATE PROPERTY twomass_xyz.k_msigcom FLOAT;
CREATE PROPERTY twomass_xyz.k_snr FLOAT;

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


CREATE CLUSTER project_cucumber;

INSERT INTO twomass_psc CLUSTER project_cucumber SET
    ...
    xyz = ( INSERT INTO twomass_xyz CLUSTER project_cucumber SET ... );

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

К какому-то из этих классов. Я не имею ни малейшего понятия что это за данные.
Описание небесных объектов.
XYZ-светимости в разных диапазонах
Ra|Dec — координаты
Где пространственный индекс будет?
Подвох здесь незатейливый.
Конечно, логично прицепить пространственный индекс к кому классу, который содержит координаты (twomass_psc). Но тогда при фильтрации по пространственному критерию придется сделать лишнее чтение, чтобы получить ссылку на класс со светимостями (twomass_xyz).

Делать (дублировать) пространственный индекс к twomass_xyz нелогично.
Хранить в пространственном индексе ссылки на все экземпляры классов — некрасиво.
Да и позволит ли это СУБД.
Иметь один индекс на всех — а кто будет синхронизировать идентификаторы в разных классах?

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

Вообще, противостояние табличных и сетевых СУБД длится столько же, сколько существуют сами СУБД. Это как противостояние брони и снаряда.
Ну да, будет дополнительное чтение. Мы же для этого всё это и затевали, чтобы часть данных хранилась отдельно. Или что вы пытаетесь добиться?

В неуникальный индекс можно по одному ключу засовывать несколько разных документов. Зачем синхронизировать идентификаторы, если они не меняются?
Почему пытаюсь, в статье описано как сделать это без дополнительного чтения.
И зачем городить огород с разными классами, когда можно всё сделать в рамках старого доброго SQL?
В сущности и по-колоночный и строчный варианты — крайние случаи одной идеи — нарезать таблицу на “ленточки“ и внутри каждой ленты хранить данные построчно. Просто в одном случае лента одна, в другом ленты вырождаются до одной колонки.

Так почему бы не допустить и промежуточные варианты — если данные некоторых колонок приходят/читаются вместе, пусть и окажутся на одной ленте. А если в ленте не оказалось данных (NULL-ы), то и хранить ничего не надо. Заодно снимается проблема максимального размера строки — можно расщепить таблицу, когда есть риск, что строка не поместится на одной странице.

Каждая дополнительная "ленточка" — это дополнительное чтение.


Прагмы — это не "старый добрый SQL". Я привёл примеры на диалекте SQL — OSQL.

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

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

Если данные лежат в разных местах и нужны, то читать несколько раз в этих разных местах по любому придётся. Если же вы чисто про индексы, то любой индекс — это дерево, а дерево — частный случай графа.
Любой индекс в описанной в статье схеме относится ко всем колонкам.
Так же как и в строчных СУБД (и в колоночных, кстати).

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

Уж не знаю как и разжевывать.
Как я уже говорил, нет проблемы в том, чтобы засунуть в индекс по одному ключу несколько документов. В том числе засовывать в индекс не сами исходные документы, а агрегирующие документы со ссылками на исходные. Рулить в таком случае индексами придётся через триггеры, да. Но и возможностей гораздо больше.
Зачем пихать в индекс идентификаторы нескольких документов,
если это один документ? Вместе с идентификатором документа придётся хранить и идентификатор таблицы/класса. Конечно можно, но зачем, если можно обойтись без этого?

… is like sleeping with your sister. Sure she's a great piece of tail with a blouse full of goodies, but it's just illegal.

Topper Harley

Если нам нужно по разному хранить части документа, то по факту это разные документы. Идентификатор документа состоит из двух частей: номер кластера и номер документа в нём. @12:345

Если вы готовы из неведомых мне соображений принести в жертву производительность, не стану мешать.
Но сам так делать не стану и никому не посоветую.

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

А зачем тогда в графовых бд предназначены специальные команды для явного создания индексов?

Логика "у индексов под капотом графы, значит для графов отдельные индексы не нужны" это примерно как "у лопаты черенок из дерева, значит чтобы в лесу выкопать яму, лопата не нужна, там и так деревья есть".
Для простоты использования главным образом. Не надо самому триггеры писать, ключ формировать и тд.
Так нужны индексы или нет? Подвижность вашей позиции удивляет. Сначала для вас приемлемо лишнее чтение,
потом вы собираетесь идентификаторы всех частей записи записывать в индексах. Теперь и индексы не нужны.

Самое время продемонстрировать хоть какие-то преимущества графового подхода. Сейчас я вижу одни только издержки.
А без «мурзилок», вот на конкретной задаче объясните,
что ваш подход может предложить такого, чего нельзя достичь обычными средствами.
Не не, это совершенно разные задачи.
Здесь речь о таблице с большим число записей (и колонок), минимизируем стоимости и создания такой таблицы и поиска по ней с фильтрацией.
Да, кстати. Предположим что у вас более 2, пусть 3 класса, описывающих то, что когда-то было одной таблицей. Какова будет топология взаимоотношений между ними?
Вот вы нарисовали одну ссылку из psc в xyz. Пусть есть еще qwe.

Циклы делать нехорошо по понятным причинам. Звезда?
Ок, у нас ссылки psc->xyz и psc->qwe. Рассмотрим select, в котором задействованы только колонки из xyz и qwe. Опять не обойтись без центрального узла с его ссылками.
Это бы вызвало либо повторный update одного из документов
либо необходимость руками управлять идентификаторами.
И то и другое не слишком красиво.

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

Обновить 2 документа — это гораздо быстрее, чем обновить один документ и индекс. Ну и если руками расставлять двусторонние ссылки влом, то можно использовать высокоуровневый API который делает это за вас.


-- руками обеспечили обратные ссылки --
UPDATE @12:34 ADD bar = ( INSERT INTO bar SET( foo = @12:34 ) );

-- автоматически создали двусторонние ссылки --
CREATE EDGE foo_bar FROM @12:34 TO ( CREATE VERTEX bar )

Насчёт "упорядоченности" я не понял. Как наличие или отсутствие ссылки может что-то там "упорядочить"?

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

Упорядоченность возникает в тот момент, когда вам надо обойти цикл.

Обновление индекса может привести к его ребалансировке и как следствие гораздо большему числу записей.


Разумеется обходить цикл вы будете в каком-то не случайном порядке. Какие вы видите в этом проблемы?

Потенциальная проблема в том, что этот порядок привнесён искусственно.
Да нет, вполне естественный порядок. А вот порядок добавления данных в базу — весьма искусственный и недеретменированный.
В исходной задаче при разбиении записей по страницам эти страницы между собой никак не упорядочены.
Если конечно не заниматься схоластикой.

В схеме «звезда» тоже порядок отсутствует, есть правда центральное звено, но это еще как-то можно интуитивно оправдать.

Идея ваша вполне понятна, не плоха и хорошо известна, но подана очень плохо.


  1. Все результат действия прагм примерно аналогичен созданию нескольких таблиц связанных по IDENTITY/ROWID. В MySQL это можно воспроизвести буквально, в СУБД с ROWID (PostresSQL) будет несколько иначе и примерно медленнее.


  2. Производительность этой схемы прежде всего зависит от отношения селектов/апдейтов, в том числе для отдельных полей. Этот момент как-то совсем не рассмотрен. Более того, это imho примерно единственная точка, с которой можно без усложнений и полноценно рассмотреть все плюсы-минусы.


  3. Добавлены отсылки к колоночным БД с их массовым перечислением, но при этом внезапно никак не упомянуты главное принципиальное отличие: Колоночные базы ориентированны на другой набор операций, поэтому как-правило не поддерживают апдейты, либо делают их крайне дорогими и без ACID. Соответственно, за счет этого могут использовать совсем другие индексы (примерно не b-tree, а зональные, битовые маски и column imprints).

В сухом остатке выходит, что в статье:


  • описание well-known модели нескольких b-tree с общим PK;
  • это описание завуалированно и подано как новый велосипед с расширением синтаксиса SQL:
  • модель нескольких b-tree с общим PK сопоставляется с колоночной, без рассмотрения самых главных отличий.

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

1) про «несколько таблиц связанных по IDENTITY/ROWID».
Конечно, так можно сделать. Конечному разработчику проще иметь дело с одной таблицей, которая где-то внутри разбита на ленты и деталей знать не надо. Не нужно следить за тем, чтобы ключи не рассогласовались. Не нужно всё обставлять скобками транзакций, если возникает например 3 insert-а вместо одного. Если я хочу в триггере использовать значение колонки из дружественной таблицы, просто беру и использую. Индексы относятся ко всем колонкам, не надо организовывать join-ы.… Разве нет?

Опять же в тексте есть ссылка на статью «ColumnStores vs. RowStores: How Different Are They Really?». В ней пытаются эмулировать колоночные таблицы в обычной СУБД. Один из основных выводов — эмуляция не даёт нам понять, есть преимущества или нет.

2) «Производительность этой схемы прежде всего зависит от отношения селектов/апдейтов, в том числе для отдельных полей». Да, это тема для будущих статей. Работа в процессе и я не готов делиться результатами.

3) Мне бы не хотелось соревноваться с колоночными СУБД на их поле. Один из вложенных в статью смыслов (не знаю, насколько удалось) в том, чтобы показать, что есть смежная зона, где можно получить преимущества обоих подходов.

PS: в статье честно сказано, что фактически новизна лишь в способе подачи подсказок SQL-процессору.

PPS: автор вероятно «не достаточно осведомлен», но для этого и существует площадка Хабра,
чтобы делиться мнениями и идеями, где можно встретить людей со знаниями в самых разных областях.
Вы вот это
INSERT INTO foo (auto,text)
    VALUES(NULL,'text');              # generate ID by inserting NULL
INSERT INTO foo2 (id,text)
    VALUES(LAST_INSERT_ID(),'text');  # use ID in second table
имели ввиду, когда говорили, что «создание нескольких таблиц связанных по IDENTITY… в MySQL можно воспроизвести буквально»?

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

Фильтрацию по индексу одной таблицы и использование значений из другой без join-а не организуешь. Сумеет ли оптимизатор всегда распознать, что первую таблицу поднимать не надо?

Constraints уровня объединённой таблицы нетривиальны и остаются на совести разработчика.

Всё это детали реализации, которыми разработчика загружать не надо,
для него это должна быть одна таблица с общими индексами и общими constraints.

Этот вариант с MySQL напоминает обслуживание паровых машин до изобретения автоматического предохранительного клапана.
Only those users with full accounts are able to leave comments. Log in, please.