Начало — см. часть 1.
Такая структура как упорядоченное дерево имеет разные частные случаи. Рассмотрим те, которые имеют практическую ценность при работе с глобалами.
Глобалы можно использовать не только подобно массиву, но и как обычные переменные. Например как счётчик:
При этом глобал, кроме значения, может ещё иметь ещё и ветви. Одно не исключает второго.
Вообще — это классическая key-value база. А если в качестве значения мы будем сохранять кортеж значений, то получим самую обыкновенную таблицу с первичным ключом.
Для реализации таблицы на глобалах нам придётся самим формировать строки из значений колонок, а потом сохранять их в глобал по первичному ключу. Чтобы при считывании было возможно разделить строку опять на колонки можно использовать:
Что интересно, не представляет труда на глобалах сделать нечто подобное вторичным индексам в реляционных БД. Назовём такие структуры индексными глобалами. Индексный глобал — это вспомогательное дерево для быстрого поиска по полям, не являющимися составными частями первичного ключа основного глобала. Для его заполнения и использования нужно написать дополнительный код.
Давайте создадим индексный глобал по первой колонке.
Теперь для быстрого поиска информации по первой колонке нам предстоит заглянуть в глобал ^i и найти первичные ключи (id) соответствующие нужному значению первой колонки.
При вставке значения мы можем сразу создавать и значение и индексные глобалы по нужным полям. А для надёжности обернём всё это в транзакцию.
Подробноcти как сделать на M таблицы на глобалах, эмуляцию вторичных индексов.
Да, реализация таблиц на глобалах выглядит более сложной, чем в реляционных БД. Поэтому промышленные БД на глобалах имеют SQL-доступ для упрощения работы с табличными данными.
Вообще, если схема данных не будет часто меняться, скорость вставки некритична и всю базу можно легко представить в виде нормализованных таблиц, то проще работать именно с SQL, так как он обеспечивает более высокий уровень абстракции.
В этом частном случае я хотел показать, что глобалы могут выступать как конструктор для создания других БД. Как ассемблер, на котором можно написать другие языки. А вот примеры, как можно создать на глобалах аналоги key-value, списков, множеств, табличных, документо-ориентированных БД.
Если нужно создать какую-то нестандартную БД минимальными усилиями, то стоит взглянуть в сторону глобалов.
Вы, наверное, догадались: это альтернативная реализация таблиц на глобалах. Сравним эту реализацию с предыдущей.
Вывод: на любителя. Поскольку скорость — одно из самых ключевых преимуществ глобалов, то почти нет смысла использовать эту реализацию, так как она скорее всего будет работать не быстрее таблиц на реляционных базах данных.
Любая структура данных, которая может быть представлена в виде дерева, прекрасно ложится на глобалы.
Это область традиционного применения глобалов. В медицинской сфере огромно число болезней, лекарств, симптомов, методов лечения. Создавать на каждого пациента таблицу с миллионом полей нерационально. Тем более, что 99% полей будут пустыми.
Представьте SQL БД из таблиц: «пациент» ~ 100 000 полей, «Лекарство» — 100 000 полей, «Терапия» — 100 000 полей, «Осложнения» — 100 000 полей и т.д. и т.п. Либо можно создать БД из многих тысяч таблиц, каждая под определённый тип пациента (а ведь они могут пересекаться!), лечения, лекарства, и ещё тысячи таблиц для связей между этими таблицами.
Глобалы идеально подошли для медицины, так как позволяют создать для каждого пациента точное описание его истории болезни, различных терапий, действий лекарств, в виде дерева, не тратя при этом лишнее дисковое пространство на пустые колонки, как было бы в реляционном случае.
На глобалах удобно делать БД с данными о людях, когда важно накопить и систематизировать максимум разнообразной информации о клиенте. Это востребовано в медицине, банковской сфере, маркетинге, архивном деле и других областях
.
Безусловно, на SQL тоже можно эмулировать дерево всего несколькими таблицами (EAV, 1,2,3,4,5,6,7,8,9,10), однако это существенно сложнее и будет медленнее работать. По сути пришлось бы написать глобал работающий на таблицах и спрятать всю работу с таблицами под слоем абстракции. Неправильно эмулировать более низкоуровневую технологию (глобалы) средствами более высокоуровневой (SQL). Нецелесообразно.
Не секрет, что изменение схемы данных на гигантских таблицах (ALTER TABLE) может занять приличное время. MySQL, например, делает ALTER TABLE ADD|DROP COLUMN полным копированием информации из старой в новую таблицу (тестировал движки MyISAM, InnoDB). Что может повесить рабочую базу с миллиардами записей на дни, если не недели.
Изменение структуры данных, если мы используем глобалы, нам ничего не стоят. В любой момент мы можем добавить любые нужные нам новые свойства к любому объекту, на любом уровне иерархии. Изменения связанные с переименованием ветвей можно запускать в фоновом режиме на работающей БД.
Поэтому, когда речь идёт о хранении объектов с огромным количеством необязательных свойств, глобалы — отличный выбор.
Причём, напомню, доступ к любому из свойств — моментален, так как в глобале все пути представляют собой B-tree.
Базы данных на глобалах, в общем случае, это разновидность документно-ориентированных БД, со возможностью хранения иерархической информации. Поэтому в сфере хранения медицинских карт с глобалами могут конкурировать документно-ориентированные БД.
Ассоциативные массивы (даже с вложенными массивами) прекрасно ложатся на глобалы. Например такой массив из PHP отобразится в первую картинку 3.3.1.
Также легко хранятся в глобалах. Для хранения можно разложить разными способами.
XML
Самый простой способ раскладки XML на глобалы, это когда в узлах храним атрибуты тэгов. А если будет нужен быстрый доступ к аттрибутам тэгов, то мы можем их вынести в отдельные ветви.
На COS этому будет соответствовать код:
Замечание: Для XML, JSON, ассоциативных массивов можно придумать много разных способов отображения на глобалах. В данном случае мы не отразили порядок вложенных тэгов в тэге note. В глобале ^xml вложенные тэги будут отображаться в алфавитном порядке. Для строго отражения порядка можно использовать, например, такое отображение:
JSON.
На первой картинке из раздела 3.3.1 показано отражение этого JSON-документа:
Примеры: структура офисов продаж, расположение людей в МЛМ-структуре, база дебютов в шахматах.
База дебютов. Можно в качестве значения индекса узла глобала использовать оценку силы хода. Тогда, чтобы выбрать самый сильный ход достаточно будет выбрать ветвь с наибольшим весом. В глобале все ветви на каждом уровне будут отсортированы по силе хода.
Структура офисов продаж, структура людей в МЛМ. В узлах можно хранить некие кеширующие значения отражающие характеристики всего поддерева. Например, объём продаж данного поддерева. В любой момент мы можем получить цифру, отражающую достижения любой ветви.
В первой колонке представлены случаи, когда вы получите существенный выигрыш в скорости при использовании глобалов, а во второй когда упростится разработка или модель данных.
Планируется продолжение «Глобалы — мечи-кладенцы для хранения данных. Разреженные массивы. Часть 3»
Disclaimer: данная статья и мои комментарии к ней является моим мнением и не имеют отношения к официальной позиции корпорации InterSystems.
3. Варианты структур при использовании глобалов
Такая структура как упорядоченное дерево имеет разные частные случаи. Рассмотрим те, которые имеют практическую ценность при работе с глобалами.
3.1 Частный случай 1. Один узел без ветвей
Глобалы можно использовать не только подобно массиву, но и как обычные переменные. Например как счётчик:
Set ^counter = 0 ; установка счётчика
Set id=$Increment(^counter) ; атомарное инкрементирование
При этом глобал, кроме значения, может ещё иметь ещё и ветви. Одно не исключает второго.
3.2 Частный случай 2. Одна вершина и множество ветвей
Вообще — это классическая key-value база. А если в качестве значения мы будем сохранять кортеж значений, то получим самую обыкновенную таблицу с первичным ключом.
Для реализации таблицы на глобалах нам придётся самим формировать строки из значений колонок, а потом сохранять их в глобал по первичному ключу. Чтобы при считывании было возможно разделить строку опять на колонки можно использовать:
- символы-разделители.
Set ^t(id1) = "col11/col21/col31" Set ^t(id2) = "col12/col22/col32"
- жёсткую схему, по которой каждое поле занимает заранее определённое число байт. Как и делается в реляционных БД.
- специальную функцию $LB (есть в Cache), которая составляет строку из значений.
Set ^t(id1) = $LB("col11", "col21", "col31") Set ^t(id2) = $LB("col12", "col22", "col32")
Что интересно, не представляет труда на глобалах сделать нечто подобное вторичным индексам в реляционных БД. Назовём такие структуры индексными глобалами. Индексный глобал — это вспомогательное дерево для быстрого поиска по полям, не являющимися составными частями первичного ключа основного глобала. Для его заполнения и использования нужно написать дополнительный код.
Давайте создадим индексный глобал по первой колонке.
Set ^i("col11", id1) = 1
Set ^i("col12", id2) = 1
Теперь для быстрого поиска информации по первой колонке нам предстоит заглянуть в глобал ^i и найти первичные ключи (id) соответствующие нужному значению первой колонки.
При вставке значения мы можем сразу создавать и значение и индексные глобалы по нужным полям. А для надёжности обернём всё это в транзакцию.
TSTART
Set ^t(id1) = $LB("col11", "col21", "col31")
Set ^i("col11", id1) = 1
TCOMMIT
Подробноcти как сделать на M таблицы на глобалах, эмуляцию вторичных индексов.
Работать такие таблицы будут также быстро как и в традиционных БД (или даже быстрее), если функции вставки/обновления/удаления строк написать на COS/M и скомпилировать.
Это утверждение я проверял тестами на массовых INSERT и SELECT в одну двухколоночную таблицу, в том числе с использованием команд TSTART и TCOMMIT (транзакций).
Более сложные сценарии с конкурентным доступом и параллельными транзакциями не тестировал.
Без использования транзакций скорость инсертов была на миллионе значений 778 361 вставок/секунду.
При 300 миллионов значений — 422 141 вставок/секунду.
При использовании транзакций — 572 082 вставок/секунду на 50М вставок. Все операции проводились из скомпилированного M-кода.
Жёсткие диски обычные, не SSD. RAID5 с Write-back. Процессор Phenom II 1100T.
Для аналогичного тестирования SQL-базы нужно написать хранимую процедуру, которая в цикле будет делать вставки. При тестировании MySQL 5.5 (хранилище InnoDB) по такой методике получал цифры не больше, чем 11К вставок в секунду.
Более сложные сценарии с конкурентным доступом и параллельными транзакциями не тестировал.
Без использования транзакций скорость инсертов была на миллионе значений 778 361 вставок/секунду.
При 300 миллионов значений — 422 141 вставок/секунду.
При использовании транзакций — 572 082 вставок/секунду на 50М вставок. Все операции проводились из скомпилированного M-кода.
Жёсткие диски обычные, не SSD. RAID5 с Write-back. Процессор Phenom II 1100T.
Для аналогичного тестирования SQL-базы нужно написать хранимую процедуру, которая в цикле будет делать вставки. При тестировании MySQL 5.5 (хранилище InnoDB) по такой методике получал цифры не больше, чем 11К вставок в секунду.
Да, реализация таблиц на глобалах выглядит более сложной, чем в реляционных БД. Поэтому промышленные БД на глобалах имеют SQL-доступ для упрощения работы с табличными данными.
Вообще, если схема данных не будет часто меняться, скорость вставки некритична и всю базу можно легко представить в виде нормализованных таблиц, то проще работать именно с SQL, так как он обеспечивает более высокий уровень абстракции.
В этом частном случае я хотел показать, что глобалы могут выступать как конструктор для создания других БД. Как ассемблер, на котором можно написать другие языки. А вот примеры, как можно создать на глобалах аналоги key-value, списков, множеств, табличных, документо-ориентированных БД.
Если нужно создать какую-то нестандартную БД минимальными усилиями, то стоит взглянуть в сторону глобалов.
3.3 Частный случай 3. Двухуровневое дерево, у каждого узла второго уровня фиксированное число ветвей
Вы, наверное, догадались: это альтернативная реализация таблиц на глобалах. Сравним эту реализацию с предыдущей.
Таблицы на двухуровневом дереве vs. на одноуровневом дереве. | |
---|---|
Минусы | Плюсы |
|
|
3.4 Общий случай. Деревья и упорядоченные деревья
Любая структура данных, которая может быть представлена в виде дерева, прекрасно ложится на глобалы.
3.4.1 Объекты с подобъектами
Это область традиционного применения глобалов. В медицинской сфере огромно число болезней, лекарств, симптомов, методов лечения. Создавать на каждого пациента таблицу с миллионом полей нерационально. Тем более, что 99% полей будут пустыми.
Представьте SQL БД из таблиц: «пациент» ~ 100 000 полей, «Лекарство» — 100 000 полей, «Терапия» — 100 000 полей, «Осложнения» — 100 000 полей и т.д. и т.п. Либо можно создать БД из многих тысяч таблиц, каждая под определённый тип пациента (а ведь они могут пересекаться!), лечения, лекарства, и ещё тысячи таблиц для связей между этими таблицами.
Глобалы идеально подошли для медицины, так как позволяют создать для каждого пациента точное описание его истории болезни, различных терапий, действий лекарств, в виде дерева, не тратя при этом лишнее дисковое пространство на пустые колонки, как было бы в реляционном случае.
На глобалах удобно делать БД с данными о людях, когда важно накопить и систематизировать максимум разнообразной информации о клиенте. Это востребовано в медицине, банковской сфере, маркетинге, архивном деле и других областях
.
Безусловно, на SQL тоже можно эмулировать дерево всего несколькими таблицами (EAV, 1,2,3,4,5,6,7,8,9,10), однако это существенно сложнее и будет медленнее работать. По сути пришлось бы написать глобал работающий на таблицах и спрятать всю работу с таблицами под слоем абстракции. Неправильно эмулировать более низкоуровневую технологию (глобалы) средствами более высокоуровневой (SQL). Нецелесообразно.
Не секрет, что изменение схемы данных на гигантских таблицах (ALTER TABLE) может занять приличное время. MySQL, например, делает ALTER TABLE ADD|DROP COLUMN полным копированием информации из старой в новую таблицу (тестировал движки MyISAM, InnoDB). Что может повесить рабочую базу с миллиардами записей на дни, если не недели.
Изменение структуры данных, если мы используем глобалы, нам ничего не стоят. В любой момент мы можем добавить любые нужные нам новые свойства к любому объекту, на любом уровне иерархии. Изменения связанные с переименованием ветвей можно запускать в фоновом режиме на работающей БД.
Поэтому, когда речь идёт о хранении объектов с огромным количеством необязательных свойств, глобалы — отличный выбор.
Причём, напомню, доступ к любому из свойств — моментален, так как в глобале все пути представляют собой B-tree.
Базы данных на глобалах, в общем случае, это разновидность документно-ориентированных БД, со возможностью хранения иерархической информации. Поэтому в сфере хранения медицинских карт с глобалами могут конкурировать документно-ориентированные БД.
Но всё равно это не совсем то
Возьмём для сравнения, например, MongoDB. В этой сфере она проигрывает глобалам по причинам:
- Размер документа. Единицей хранения является текст в формате JSON (точнее BSON) максимального объёма около 16МБ. Ограничение сделано специально, чтобы JSON-база не тормозила при парсинге, если в ней сохранят огромный JSON-документ, а потом будут обращаться к нему по полям. В этом документе должна быть сосредоточена вся информация о пациенте. Мы все знаем какими толстыми могут быть карты пациентов. Максимальный размер карты в 16МБ сразу ставит крест на пациентах, в карту болезней которых включены файлы МРТ, сканы ренгенографии и других исследований. В одной ветви глобала же можно иметь информацию на гигабайты и терабайты. В принципе на этом можно поставить точку, но я продолжу.
- Время сознания/изменения/удаления новых свойств в карте пациента. Такая БД должна считать в память всю карту целиком (это большой объём!), распарсить BSON, внести/изменить/удалить новый узел, обновить индексы, запаковать в BSON, сохранить на диск. Глобалу же достаточно только обратиться к конкретному свойству и произвести с ним манипуляции.
- Быстрота доступа к отдельным свойствам. При множестве свойств в документе и его многоуровневой структуре доступ к отдельным свойствам будет быстрее за счёт того, что каждый путь в глобале это B-tree. В BSON же придётся линейно распарсить документ, чтобы найти нужное свойство.
3.3.2 Ассоциативные массивы
Ассоциативные массивы (даже с вложенными массивами) прекрасно ложатся на глобалы. Например такой массив из PHP отобразится в первую картинку 3.3.1.
$a = array(
"name" => "Vince Medvedev",
"city" => "Moscow",
"threatments" => array(
"surgeries" => array("apedicectomy", "biopsy"),
"radiation" => array("gamma", "x-rays"),
"physiotherapy" => array("knee", "shoulder")
)
);
3.3.3 Иерархические документы: XML, JSON
Также легко хранятся в глобалах. Для хранения можно разложить разными способами.
XML
Самый простой способ раскладки XML на глобалы, это когда в узлах храним атрибуты тэгов. А если будет нужен быстрый доступ к аттрибутам тэгов, то мы можем их вынести в отдельные ветви.
<note id=5>
<to>Вася</to>
<from>Света</from>
<heading>Напоминание</heading>
<body>Позвони мне завтра!</body>
</note>
На COS этому будет соответствовать код:
Set ^xml("note")="id=5"
Set ^xml("note","to")="Саша"
Set ^xml("note","from")="Света"
Set ^xml("note","heading")="Напоминание"
Set ^xml("note","body")="Позвони мне завтра!"
Замечание: Для XML, JSON, ассоциативных массивов можно придумать много разных способов отображения на глобалах. В данном случае мы не отразили порядок вложенных тэгов в тэге note. В глобале ^xml вложенные тэги будут отображаться в алфавитном порядке. Для строго отражения порядка можно использовать, например, такое отображение:
JSON.
На первой картинке из раздела 3.3.1 показано отражение этого JSON-документа:
var document = {
"name": "Vince Medvedev",
"city": "Moscow",
"threatments": {
"surgeries": ["apedicectomy", "biopsy"],
"radiation": ["gamma", "x-rays"],
"physiotherapy": ["knee", "shoulder"]
},
};
3.3.4 Одинаковые структуры, связанные иерархическими отношениями
Примеры: структура офисов продаж, расположение людей в МЛМ-структуре, база дебютов в шахматах.
База дебютов. Можно в качестве значения индекса узла глобала использовать оценку силы хода. Тогда, чтобы выбрать самый сильный ход достаточно будет выбрать ветвь с наибольшим весом. В глобале все ветви на каждом уровне будут отсортированы по силе хода.
Структура офисов продаж, структура людей в МЛМ. В узлах можно хранить некие кеширующие значения отражающие характеристики всего поддерева. Например, объём продаж данного поддерева. В любой момент мы можем получить цифру, отражающую достижения любой ветви.
4. В каких случаях наиболее выгодно использовать глобалы
В первой колонке представлены случаи, когда вы получите существенный выигрыш в скорости при использовании глобалов, а во второй когда упростится разработка или модель данных.
Скорость | Удобство обработки/представления данных |
---|---|
|
|
Планируется продолжение «Глобалы — мечи-кладенцы для хранения данных. Разреженные массивы. Часть 3»
Disclaimer: данная статья и мои комментарии к ней является моим мнением и не имеют отношения к официальной позиции корпорации InterSystems.
4dmonster
Скажите пожалуйста а ваше утверждение, что у варианта «Двухуровневое дерево» будет «Быстрее доступ к значениям отдельных колонок, так как не нужно парсить строку.» проверено на каких объёмах данных и на каких паттернах данных?
inetstar
Это теоретическое предположение, основанное на понимании внутренней структуры глобалов.
Я предполагаю, что оно будет справедливо на числе колонок большем, чем 10. Или на объёме колонки с несколькими столбцами большем 10KБ.
Будет интересно увидеть комментарии от сотрудников InterSystems, которые имеют дело с глобалами каждый день.
4dmonster
Как раз теоритически всё будет зависить от паттерна данных и настроек хранения глобалов(думаю что их в кашэ можно менять каки в gt.m).
Может проведёте тесты?
inetstar
А почему бы и нет. Уже на 2-х колонках выигрыш 11,4%. При чтении 100M значений. Чем больше колонок или объём полей, тем больше будет выигрыш 2-х ярусного дерева. Тестировал в GT.M.
inetstar
Тестовый код на M такой:
Второй подход побеждает.
4dmonster
Но это кажется неверный пример для сравнения. Должно быть что-то типа:
inetstar
В моём случае я сравнивал скорости чтения.
Если делать по вашей методе — мы включим в общее время ещё и время вставки.
На таких значениях никакие кеши уже не сработают и поэтому разница будет микроскопическая.
Так как начнут шуршать винты, которые будут конкретно замедлять систему.
Если рассматривать с точки зрения, что у нас настолько большая БД, что никакие данные не закешированы, то разница будет, дай бог, в 1-2%.
В своём тесте я специально читал одну ячейку, чтобы исключить фактор винтов и проверить только скорость чтения свойства.
То, что на вставку будет медленнее второй вариант — очевидно.
vedenin1980
Вообще-то, в большинстве баз данных такие чтения одной ячейки миллион раз кэшируются, неужели нет в языке возможности замерить время конкретных циклов чтения? Каких нибудь getTime функций?
inetstar
В моём тесте тоже всё закешировалось. И в первом и во втором, поэтому я посчитал, что можно доверять. Тем более практика совпала с теорией.
Мы исключили время на перемещение головок и чтение с поверхности, и осталось только время разбора структуры глобала и парсинга строки.
vedenin1980
Тогда что вы вообще мерили? В первом тесте запрос к дереву один, но идет обработка результатов в памяти, во втором запросов к дереву два (точнее запрос идет к двум деревьям), но нет обработки результатов в памяти.
Естественно, если все запросы к деревьям закешировались, второй будет всегда работать быстрее, так как в нем просто читается значение кэша, а в первом читается значение кэша и идет ещё какая-то обработка. Да, логично что получить константу из памяти проще чем получить константу и ещё с ней что-то делать, только как часто в реальном приложении нужно миллион раз работать с одной константой?
Так же естественно, что в реальном приложении скорее всего проиграет второй запрос, который будет дважды перемещать головки и читать с диска, в то время как первый лишь один раз, а дальше работать только с памятью.
inetstar
Вы что-то путаете. Тот код, который вы прокомментировали, предложил 4dmonster. Мой код — первый в треде.
У глобалов кешируются не значения, а блоки базы.
Вопрос был в том, что быстрее сработает — проход по двухуровневому дереву, или по одноуровневому с парсингом строки.
А измерялось это для того, чтобы доказать моё утверждение о том, что доступ к отдельным свойствам у второго типа таблиц на глобалах быстрее. В реальных базах кеш широко используется. И все частоиспользуемые области в нём.
vedenin1980
Ещё раз, категорически неверно брать значение одной ячейки миллион раз и считать что это тест, так как этот тест ничего реального не покажет. Ведь вы же не берете в реальных приложениях значение одной ячейки столько раз? Поэтому сначала заполняете миллион значений в таблице, а потом тестируйте чтение из каждой ячейки. Чтобы чтение не влияло, заполняете таблицу ДО замеров времени теста.
inetstar
Я тестировал скорость доступа к отдельному свойству без влияния HDD.
Доступа к закешированному значению. Это было сознательно.
Если влезет винт, то разница будет маленькой, незаметной. Это вопрос чисто теоретический, нужный только для сравнительной таблицы.
vedenin1980
Вы не тестировали вообще НИЧЕГО. Плохо, что не понимая даже основ тестирования, вы пытаетесь писать статьи и что-то кому-то доказывать.
4dmonster
Вот код:
inetstar
Я тоже не спец в M. Вообще в вашем тесте есть методологическая погрешность: дело в том, что нужно чтобы M полностью сбрасывал буфера перед тем как проводить чтение. Во втором случае буфера значительно больше, и поэтому они сбрасываются во время чтения, что приводит к тому, что на чтение второй подход проиграл секунду.
Т.е. нужно было сделать программки на заполнение глобалов отдельными.
Тест на запись: нужно мерять время не внутренними функциями, а командой линукса time. Тоже из-за буферов. Так как ваш тест может показать что работа выполнена, а на самом деле буфера ещё не сброшены.
Далее: вообще-то из ваших тестов видно, что mytest2 быстрее, как я и говорил.
То что на вставку 2-я реализация таблиц будет медленнее — это я тоже говорил.
Теперь смотрим результат по чтению: 88 и 89 секунд — слишком мало отличие. Если сделать тесты на чтение отдельными программками, то тогда вы увидите истинный результат.
4dmonster
mytest — это ваш подход и по нему кажется что $PIECE отстаёт.
А msrtest — мой подход. И кажется наоборот.
Я учту пункты про буферы и малое различие в выигрыше $PIECE. И выложу что получилось.
tsafin
FWIW
Вот результаты того-же самого на Haswell-ULT ноутбуке (в режиме экономии батареи) под Cache' 2015.2
inetstar
Это я очень осторожно ответил. На самом деле я думаю, что выигрыш уже будет с 2-х колонок. Так как для поиска нужного индекса (substring) достаточно линейно просмотреть несколько байт (так устроен глобал), а для парсинга строки придётся выделить память под всю строку, распарсить строку, выделить память под нужное поле.
lair
Нет, ну опять? Вы можете это утверждение чем-нибудь подтвердить, тестами там какими-нибудь?
В чем преимущество глобалов по сравнению с другими БД для «создания нестандартных БД»? Предположим у меня в проекте уже есть три разнотипных БД (Event Store, RDBMS, key-value) — что я выиграю, добавив в этот зоопарк еще и глобалы?
Типично неправильно декомпонованная предметная область. Не нужно там тысяч полей. Достаточно представить ту же информацию не в виде дерева, а в виде E-R, как станет видно, как ее реализовывать.
Ага. Вот только
Да-да-да-да, конечно же.
Ну так индексы же.
Но в принципе, спор «иерархический kv против документо-ориентированой БД», очевидно, определяется сценарием использования. Если обычно документ достается для обработки целиком (например, возможно, врач открывает карту пациента перед приемом), то ДОБД неплохо справится. Если чаще нужны конкретные части — то нужна декомпозиция, и дальше начинаются варианты (кстати, ДОБД тоже неплохо декомпонуются, тут все упирается в транзакционную семантику).
Вот только в них легко можно встретить вложенность больше 31 уровня. И что делать?
И все-таки я не понимаю, что такое «нестандартная БД». Ну или наоборот, что такое «стандартная»?
inetstar
В прошлом посте я приводил свои тесты на вставку на 1M и 100M значений. Это аналогично двухколоночной таблице.
Выигрыш: в конце статьи есть таблица, где показано для каких случаев вы получите выигрыш.
Хранить целиком, без декомпозиции на глобал. Как и делают JSON СУБД. Или использовать специализированную JSON СУБД.
lair
С индексами? А если полей порядка пятидесяти, индексов полтора десятка, и из них половина — покрывающие (что и активно используется запросами)?
А ограничение на размер?
inetstar
Тут всё просто. В двухколоночном варианте достаточно 1-го дерева. В этом варианте глобалы выигрывают по вставке. А в таблице также строится 1 индекс (тоже B*-tree)
Для эмуляции каждого дополнительного индекса потребуется ещё дерево. Но оно также будет строиться для индекса в реляционной базе.
Число индексов и индексных глобалов будет одинаковым. Вывод: глобал будет быстрее.
Глобал, сам по себе, более низкоуровневая технология, поэтому тут нет ничего удивительного.
lair
Не, не убедили. Вы сравниваете яблоки с бочками, и говорите, что яблоки дешевле.
Во-первых, вы исходите из того, что деревья в глобалах и индексы в реляционных базах работают с одинаковой скоростью. Но почему? На чем основано это убеждение?
Во-вторых, вы совершенно забываете про сценарии массовых операций, где в глобалах вам придется иметь дело с каждой записью отдельно, а в реляционной БД — группой, со всеми вытекающими отсюда оптимизациями.
Так что тесты и только тесты, и ничего кроме тестов.
(И это мы еще не затронули тот занимательный вопрос, сколько кода вам придется написать, чтобы это реализовать, и сколько ошибок будет при его поддержке. Кто говорил «не надо разрабатывать свою БД»?)
inetstar
Уже скоро язык отвалится объяснять. Когда я делал таблицу с одним индексом и глобал из одного дерева глобал был в разы быстрее.
Всё я помню. С группой — это только в SQL. А уровнем ниже будет тот же поиск строк по дереву и та же обработка строк.
Если бы вы прочитали мою статью, то увидели бы, что я рекомендую работать с табличными данными через SQL.
lair
И почему вы считаете, что этот ваш опыт масштабируется?
Какая обработка строк, о чем вы? Не говоря уже о том, что когда мы имеем дело с группой операций, мы можем оптимизировать наши действия (начиная с банальной преаллокации нужного места и заканчивая последовательной, а не рандомной вставкой в индекс).
Что и говорит нам о том, что не надо реализовывать табличные структуры на глобалах, ибо незачем. А SQL-решений вокруг и так хватает, чего уж.
inetstar
Тоже уже объяснил. Каждый индекс эквивалентен индексному глобалу. Если на одном выигрывает, то и на 10 выиграет.
Например UPDATE t SET a=a+b WHERE b=5
Нужно найти строки по индексу b (если есть), а далее каждую строку обработать. Какие тут можно сделать улучшения, которые невозможны для глобалов?
lair
Вы почему-то считаете, что это линейная зависимость. Почему бы? Никаких доказательств.
Тесты, только тесты.
Например, получить в качестве результата прохода по индексу не значение основного ключа, а сразу физический адрес, где это лежит.
Вот вам контрпример:
По City — индекс IX_City. Первичный ключ не указан и создается автоматически. Так вот, вместо того, чтобы делать столько рандомных вставок в IX_City, сколько у нас записей вставлено, мы можем собрать массив туплов (City, ID), сгруппировать его по City, а затем вставить получившиеся группы, тем самым уменьшив количество проходов по индексу.
Вы, конечно, можете сделать это на глобалах. Руками. Для каждой отдельной операции, с которой вы столкнетесь.
inetstar
Я точно также могу получить выборку отсортированную по City и последовательно её вставить. Никаких проблем.
Только я таблицы на глобалах буду использовать в крайнем случае, когда у меня 99% глобалов и тут нужен 1% таблиц.
Руками кодить не хочу. Делать таблицы на глобалах — это принципиальная возможность, а не рекомендованная. В статье об этом написано.
lair
… и дальше снова будет вопрос, почему вы считаете, что ваши операции (которые в пользовательском коде) равноэффективны операциям в ядре RDBMS.
Тесты, тесты, тесты. Ничего кроме тестов.
inetstar
Это не пользовательский код. А код, который компилируется в машинные коды у GT.M, и в байт-коды в Cache (там как и в Java есть своя виртуальная машина).
Обычный SQL — это интерпретатор (хранимые процедуры у всех по разному). А COS/M компилируется. Две большие разницы.
lair
Он все равно пользовательский, а не системный.
А обычный SQL тут никого не волнует. Обычный SQL закончился в тот момент, когда мы сказали INSERT, а парсер на той стороне это обработал. А все индексы — они уже внутри ядра, сильно позже и на другом уровне.
inetstar
Чем отличается скомпилированный в машинные коды пользовательский код от системного?
lair
Оптимизациями, конечно. Вы же утверждали, в свое время, что программа, написанная на ассемблере, быстрее, чем программа, написанная на ЯПВУ. А компилируются они в одни и те же машинные коды. Вот и тут то же самое: то, на чем вы пишете работу с глобалами — это ЯПВУ.
(не говоря уже о том, что пользовательский код работает только с дозволенными абстракциями, а системный — с теми, с которыми хочет системный программист)
inetstar
Код, который написан на COS/М, компилируется в высокооптимизированный системными программистами код. Было бы иначе, я бы не получил 789К/сек инсертов в «пользовательском» коде.
lair
Вы это серьезно? Вы правда не понимаете, что ваш ^SET компилируется просто в вызов системной (предоставленной движком) функции?
inetstar
А такое ощущение, что вы не можете догнать. Даже, если и вызывается функция движка, а не inline она в коде, то эта функция и есть высокооптимизированный системными программистами код.
lair
А теперь задумайтесь о разнице между десятью вызовами «высокооптимизированного кода», и одним вызовом «высокооптимизированного кода» (выполняющего такую же работу, как те десять). Во втором случае, очевидно, больше пространство для оптимизаций.
inetstar
Вообще неочевидно.
В таком случае бы все высокоуровневые языки, где каждая команда выполняет много работы, были бы быстрее низкоуровневых.
Больше пространство для оптимизаций там, куда можно залезть руками.
Ну и глобалы работали бы медленнее таблиц. Но по моим тестам всё не так. Так что тестируйте, тестируйте…
lair
Вообще никак не связано.
Так там и залезли.
Как по вашему, что эффективнее:
или
inetstar
Если первое написать написать на ASM, а второе на Basic, то первое.
У какого SQL, из которых вы знаете, есть компиляция SQL-запросов в машинные коды? Или хотя бы в байт-коды с возможностью сохранения между сессиями?
lair
Это один и тот же язык, если что.
А зачем мне компилировать SQL-запрос в машинные коды?
Вот смотрите. У меня есть таблица на, скажем, полсотни полей, с парой десятков индексов. Я делаю в нее один инсерт:
INSERT INTO Target VALUES (...)
. Это одна команда, пятьдесят один аргумент (имя таблицы + значения колонок). Эта команда превращается в один вызов системной функцииблаблаблаPerformInsert
с этими же 51 аргументом. Дальше внутри этой функции программист БД (который, наверное, умнее меня и лучше знает ее внутренности) может делать что угодно, применять любые оптимизации, напрямую работать с физическими адресами и так далее. Его воля.А теперь все то же самое для псевдотаблиц на глобалах: нужно сделать порядка пятидесяти вставок в глобал с данными, плюс пара десятков вставок в «индексы». Порядка семидесяти независимых вызовов системной функции «вставка в глобал», где каждый из них не знает о соседнем (и, как следствие, не может делать кросс-оптимизации).
inetstar
70 вставок я вот делать не буду. Согласно моему тексту это медленный подход.
Я сделаю 1 вставку с сочетании с функцией $LB. А на одной вставке, как я уже многократно рассказывал, глобалы быстрее.
lair
А как же индексы?
Ну и после этого вы (по вашим же утверждениям) потеряете гибкость схемы и скорость чтения отдельных элементов.
lair
Простой вопрос: что быстрее, команда
INSERT
из Cache SQL, или аналогичная по функциональности «процедура», написанная вами на M или COS/M?(естественно, мы считаем, что функциональность идентична — один набор и структура данных, индексы, транзакции, блаблабла)
4dmonster
Скорее всего так:
Если требуется абсолютная функциональная идентичность — они будут одинаково работать.
А если достаточно того, чтобы потом Cache SQL мог бы сделать SELECT — тогда написанная на M или COS/M.
tsafin
Недавно проводил тестирование «для одного большого банка», где, конечно же, мы проверяли в очередной раз разницу между CacheSQL vs Direct Global Access. В 2015.1 разница, для того простого случая что мы смоделировали, в разы (x2-x3), для 2015.2 на примерно 15%.
Т.е. сейчас, в 2015.2, я легко пожертвую 15% ухудшения по сравнению с идеальным случаем на голом глобале, но буду использовать более высокий уровень абстракции. Ибо так проще в долговременном плане.
lair
Т.е. CacheSQL медленнее? Это при полном сохранении функциональности?
inetstar
А что удивительного в том, что более высокоуровневое средство (CacheSQL) медленнее низкоуровневого прямого доступа к глобалам?
lair
Я уже объяснял это выше, повторяться не буду.
tsafin
Да, в случае голой заливки данных (я не говорю про запросы), INSERT в CacheSQL всегда будет медленнее чем прямая манипуляция глобалом, т.к. добавляется некоторый объем дополнительной работы необходимый для эмуляции ACID. Программист, работающий с глобалом напрямую обычно игнорирует некоторую транзакционную строгость, выигрывая по скорости. Иногда, при первичной загрузке данных, или в монопольном режиме на это можно закрыть глаза.
(Ну т.е. нет, конечно, полного сохранения функциональности, COS/M программист обычно дяже не знает о таких деталях о которых должен заботиться транслятор SQL в Cache' для правильной поддержки транзакционной целостности в SQL)
Еще раз, относитесь к прямому доступу к глобалам в Cache'/GT.M как к «ассемблеру», а SQL/объектной обертке в Cache' как C/C++. Большие системы правильнее писать, применяя абстракции более высокого уровня, но иногда, на критичном участке, «можно и на ассемблере пописать».
lair
Тогда это не то, что меня интересовало. Я изначально ставил вопрос о сохранении функциональности. Понятно, что если мы выкинем оптимизации под чтение и ACID, то простые операции вставки дешевле, чем более сложная структура, но автор-то утверждал, что даже в режиме функциональной имитации глобалы все равно быстрее:
Вот этот компромис мне очень хорошо понятен, да. Одним пожертвовали, другое выиграли. Меня просто второй пост подряд удивляют (неявные) утверждения, что глобалы — это выиграй все, пожертвовав ничем.
inetstar
Вы сами признали уровень изоляции аналогичным snapshot (что весьма высокий уровень), а также с ACID (вопрос не в том, есть ли в глобалсах ACID. Вопрос в том, какой именно).
Я не понимаю, зачем вы теперь отказываетесь от своих слов.
Никто ACID не выкидывает. И про какие оптимизации, которые на уровне глобалов, сделать принципиально невозможно тоже не ясно.
lair
Ну вы уж как-нибудь обсудите это с tsafin, который утверждает, что INSERT добавляет дополнительный объем работы именно на ACID, и что в его тестах функциональность INSERT и прямой работы с глобалами отличается.
Что возвращает нас к вопросу — а какова же будет сравнительная скорость «ручных» операций на глобалах и INSERT с одинаковой функциональностью? (не умозрительно, а в реальных тестах)
Я не отказываюсь от своих слов, просто вы почему-то считаете, что если вы в одном сценарии доказали, что ACID достижим, то он (ACID) автоматически будет везде. Удивительно, но реальный сотрудник InterSystems утверждает обратное.
4dmonster
Вероятно, tsafin имел в виду ACID для слоя SQL а inetstar ACID для самих глобалов.
lair
А в чем фундаментальные отличия?
4dmonster
SQL слой накручен поверх глобалов и, вероятно, требует дополнительных условий / проверок для своей работы.
Например, для ссылочной целостности.
lair
До ссылочной целостности мы даже не доходили еще, пока ограничились одной таблицей и индексами.
Я, на всякий случай, повторю предмет спора: inetstar утверждал, что таблицы, написанные на самих глобалах вручную (примеры есть в статье), не медленнее, а то и быстрее, чем таблицы в традиционных БД. Я утверждаю, что при сохранении функциональности для этого нет никаких логических оснований, и я хотел бы увидеть подтверждающие утверждение inetstar тесты.
Ключевой пункт тут — при сохранении функциональности. tsafin, приводя свои результаты, признал, что функциональность различна.
4dmonster
Это зависит от того какая функциональность имеется в виду. Ну и конечно от того, ограничиваемся ли мы одной таблицей и её индексами.
lair
Бинго. Что возвращает нас к вопросу методики тестирования (впрочем, чего уж, тестов-то нет никаких).
4dmonster
Именно!
Вот для MVC фреймворков на JavaScript придумали задачу ToDo, и на ней демострируют.
Тут надо также.
Может автор в следующей статье дойдёт до конкретных примеров и можно будет сравнить что-то.
inetstar
Я в статью вставил спойлер, где несколько раскрыл на основании каких тестов я утверждаю, что таблицы на глобалах будут работать также или быстрее.
И я нигде не постулировал абсолютно идеентичную функциональность таблиц в SQL-базах и таблиц на глобалах, на которую вы так напираете.
Например, в моих тестах я не делал защиты от вставки строки с тем же ID. Это, наверное, бы замедлило бы мои тесты на несколько процентов.
Глобалы — низкоуровневое средство, дающее отличные скоростные характеристики.
Я специально сказал в статье, что с таблицами лучше работать на SQL. Удобнее.
Программист на глобалах должен знать, что о всех их потенциальных возможностях. В том числе и о той, что на глобалах можно сделать таблицы.
СтОит или не стоит — вопрос уже другой. Если нужно сверхбыстро сохранять информацию, идущую от какого-то источника, то похоже что стоит.
А если вам нужны конкурентные и очень хитрые транзакции над табличными данными, то не факт. Незачем программировать вручную, то что уже запрограммировано в SQL.
lair
Угу.
И с чем вы это сравниваете?
Тогда какой смысл говорить, что вы реализовали «таблицы» или вообще сравнивать производительность?
Ну да, есть какая-то операция вставки чего-то куда-то, которая в глобалах выполняется за x. Есть какая-то другая операция вставки чего-то куда-то, которая в другой СУБД выполняется за y. x < y… ну и что? Апельсин легче танкера с нефтью, это правда.
Зачем для этого «таблицы»? Для таких задач лучше всего append-only хранилища.
Бинго.
inetstar
Добавил в спойлер сравнение с MySQL. Хранимая процедура с циклом, котрая делает вставки — 11К вставок в секунду.
lair
Операции вставки в глобалах и MySQL семантически идентичны? Если нет, то в этих цифрах нет никакого смысла.
inetstar
Идеентичны.
Set ^a(10) = «blabla»
INSERT INTO a(id, txt) Values(10, «blabla»)
lair
Во-первых, для RDBMS это вырожденный пример, в котором она заведомо лишена смысла. Сравнивайте с KV, потому что это все, что вы тут делаете. Чтобы говорить о «таблицах», нужно реализовывать поведение реляционной системы.
Во-вторых, выше этого сравнения вы говорите об имитации индексов через глобалы. Ваша фраза «Работать такие таблицы будут также быстро как и в традиционных БД (или даже быстрее)» относится к каким «таким» таблицам — с имитацией индексов или без?
inetstar
К обоим вариантам. Но таблицы с несколькими индексами я не тестировал.
lair
… и возвращаемся к началу — к вопросу о том, почему вы считаете, что если вы протестировали часть функциональности, то вы имеете право распространять свои выводы о производительности на всю функциональность.
inetstar
В любом случае таблица с 2-мя индексами быстрее работать не станет, а вставка в глобал замедлится в 2 раза. Допустим даже в 3, с учётом транзакции.
Учитывая 70-ти кратное преимущество по скорости при одной вставке в глобал, результат будет таким же — глобалы победят. В сравнении с MySQL.
lair
Угу, во-первых, в сравнении с MySQL. Во-вторых, только до определенного количества индексов. Ну и в-третьих — только если ваши предположения о динамике верны.
inetstar
Ещё я хочу напомнить, что я говорил о скорости эмулированных таблиц, я говорил не про CacheSQL, подразумевая чистый MUMPS.
Я предполагаю что CacheSQL в интерактивном режиме — как и любой SQL — интерпретаторный язык. Не вижу ничего удивительного, что он на 15% медленнее глобалов, программы для работы с которыми компилируемы.
lair
То есть вы утверждаете, что 15% разницы (а в предыдущей версии — 200-300% разницы) между CacheSQL и прямым обращением к глобалам возникают только из-за разницы «интерпретаторный-компилируемый»?
inetstar
Думаю, что это одна из причин. Я не знаю как внутри работает CacheSQL.
lair
А еще CacheSQL может исполняться во встроенном режиме, где, как утверждается," SQL statements are converted to optimized, executable code at compilation time".
Интересно, какой из режимов тестировал tsafin, и есть ли существенная разница по производительности между одни и другим.
tsafin
Ок, попытаюсь объяснить.
Высокоуровневая идея всегда применяемая во всех надстройках Cache' — любой другой язык должен d bnjut транслироваться в INT-код (Cache' ObjectScript) и объектный код, вне зависимости от количества препроцессинга и кодогенерации. Будь это SQL, манифест инсталлятора или описание домена iKnow. В случае компиляции встроенного SQL работает макро-препроцессор (так получилось исторически), в большинстве других случаев работают методы генераторы.
(Вам не удастся встроиться в препроцессор, но генераторы, если надо, Вам помогут реализовать еще какой встроенный доменный язык для любой вашей цели. Степеней свободы достаточно)
Возвращаясь к SQL — кроме упомянутого уже выше и ниже встроенного SQL (embedded SQL), компилируемого препроцессором в низкоуровневый COS в момент компиляции MAC программы или класса, есть еще и динамический SQL, компилируемый в похожую программу в момент первого исполнения.
Если вам интересно посмотреть, то вот упрощенный пример:
1. Создадим персистентный класс
И для такого простого метода со встроенным SQL выражением:
после компиляции получаем следующий сгенерированный код (Ctrl+Shift+V в Студии):
Я не представляю себе COS программиста, который будет в своем коде так заботиться об уровнях транзакции, блокировках, откатах и обработке ошибок. COS/M код, созданный человеком, будет значительно короче и проще, и не будет заботиться (скорее всего) о всех граничных условиях. Хотя и не факт, что будет более читабельным (вон смотрите, там даже комментарии иногда генерируются :) )
В любом случае, прошу запомнить — у нас нет интерпретации при работе с SQL, транслятор SQL генерирует COS код, и, при разборе запросов, применяет все полагающиеся оптимизирующие эвристики. SQL это не просто и борьба за улучшения в генерируемом коде происходит постоянная.
P.S.
Большая просьба к участникам дискуссии — давайте попытаемся не выходить за пределы компетенции, и, в любом случае, попытаемся вести себя уважительно.
Мне нравится тенденция, наблюдаемая у некоторых участников дискуссии, пойти и почитать документы про Cache' (что я всячески приветствую), и давайте все остальные вопросы, которые возникнут по результатам тестирования задавать нам по другим каналам (письмом или через социальные сети). С удовольствием ответим!
mird
То есть все же полная функциональность не сохраняется? Так зачем вы об этом пишете?
laphroaig
>> Вот только в них легко можно встретить вложенность больше 31 уровня. И что делать?
На самом деле не легко, если только искусственно создать или спецом не оптимально, даже в самых монстрах не будет и 20. А 31 уровня более чем достаточно чтобы систематизировать все знания человечества.
lair
Вы просто не встречались с, прости г-ди, энтерпрайзным xml, где внутри подряд три вложенных конверта, а только потом данные начинаются.
Это же не структура классификатора, это программные данные, а там всякое случается.
tsafin
А вот здесь подробнее, пожалуйста! Ну 3 вложенных конверта, и данные, как тут больше 31 уровня подындексов получается?
Можно примерчик (чисто для развития)
vedenin1980
Вложенность html тегов в произвольной странице сети может зашкаливать и за 50 уровень, при использовании кучи div'ов и table для верстки.
tsafin
Это я понимаю, но я спрашивал про другой тип данных — xml структуру данных, представляющее собой сериализацию (в xml, json, yaml, toml, что-нибудь еще более модное) какой-то определенной иерархии объектов с полями и с вложенностью. Что предполагает немного другой сценарий использования, и больший, скажем так, «порядок».
Хотелось бы посмотреть именно на «enterprise xml» с адской вложенностью, чтобы понять где наши ограничения будут упираться в пределы движка.
vedenin1980
Ну вы же понимаете, что вам никто реальный «enterprise xml» реальной компании никогда не покажет. Ну примерно, в одной фирме где я работал xml выглядели как-то так (сознательно извратил насколько мог, но принцип понятен, причем в нем может быть бесконечная вложенность устройств друг в друга):
inetstar
Собственно говоря, это тоже SOAP. Не вижу смысла такое декомпозировать на глобалы. Подобного рода сообщения удобнее хранить целиком.
lair
… а потом вам надо в этом «хранить целиком» найти ответ, в котором содержится узел, удовлетворяющим конкретным критериям. Желательно все-таки не полным перебором и десериализацией.
(и да, это реальный сценарий использования, я такую систему разработал и внедрил)
inetstar
И как же вы реализовали быстрый доступ к отдельным узлам XML-документа? Разве не парсингом с использованием библиотек?
lair
Конечно, нет. Взял MS SQL, у того встроенная поддержка XML, дальше просто строил запросы на смеси T-SQL и XPath.
inetstar
XPath — это и есть библиотека, которая парсит XML.
lair
Конечно, нет. XPath — это язык запросов к XML.
vedenin1980
Это обычная inventory.xml, описывающая обычную сеть обычной распределенной компании. Иногда уровни вложенности могут быть бесконечными, особенно если там прибавить связи разными сервисами в облаках, один из которых указывает на другой и т.д… Это рядовая задача энтерпрайза.
inetstar
Разве есть смысл такое декомпозировать на глобалы для быстрого доступа к отдельным узлам?
Глобалы и XML — это специфичные ситуации, а не волшебная пилюля от всего.
vedenin1980
Если вы хотите конкурировать с mongoDB, то естественно нужно. Смысл mongoDB в том что она очень быстро записывает и отдает как целый документ, так и его отдельные части. Предлагать клиентам вон ту структуру данных пишите нам, а с вон с той идите лесом в другую базу — верный способ их потерять. Проблема в том что ваши глобалы предлагают либо писать все целиком, либо отдельными узлами, но при этом с ограничениями вложенности, размера и не факт что сборка целого json'a документа будет быстрой.
inetstar
Отдельные части Mongo не сможет отдать так же быстро как глобалы, из-за того, что придётся парсить документ и вынимать из него отдельную часть. Я не проводил тестов. Мне это ясно из устройства баз. Моё мнение.
У Mongo для JSON свои сильные преимущества. К ней не нужно реализовывать свой язык запросов.
Глобалы тоже прекрасно хранят JSON, но для этого нужно писать свой код или использовать EWD, например.
Этот пост вообще не для клиентов. Просто о возможностях глобалов для разработчиков.
vedenin1980
Боюсь ваше устройство баз весьма слабое, в монге храниться не обычный JSON, специально проиндексированный и заранее распарсенный BSON (как я понимаю с индексом с помощью того же бинарного дерева). Так что чье кун-фу сильнее должны решать тесты, а не чье-то мнение о неправильном устройстве баз.
inetstar
Я же писал о том, что это BSON. Будьте внимательнее.
И читал устройство формата BSON, который нужно парсить программно. В BSON не используются внутри B*-tree, и это не словарь со сложностью доступа O(1). Парсинг нужен.
Больше вложенности — больше парсинга.
lair
Возьмем простой SOAP (неймспейсы я повырезаю):
Посмотрим, сколько уровней нам надо для его представления:
(Откуда нули? Из того банального соображения, что элементов с одним наименованием может быть много, а глобалы, насколько я понял, не умеют несколько одинаково названных подиндексов. Ну и порядок тегов тоже имеет значение. Плюс надо атрибуты отдельно выносить, для них тоже поможет, благо, вот они не имеют порядка.)
Итого один уровень вложенности в XML дает нам два индекса, соответственно, чтобы получить 31 уровень, нужно, округляя, 16 уровней вложенности.
Вот вам пример сообщения в СМЭВ при обмене с одним малоизвестным ведомством:
Я насчитал 13 уровней. И это не самый заковыристый вариант (в нем только три конверта — SOAP, СМЭВ, ведомство, в реальности я видел ситуации, когда внутри конверта ведомства лежал следующий конверт с семантикой «пакет», а еще дальше — отдельный документ со структурой). И да, в СМЭВ 3 будет веселее, потому что там другие требования к структуре объектов.
Для пущего развлечения хочу заметить, что в этой же структуре легко могут быть бинарные элементы объемом до 5 (а иногда и больше) Мб.
tsafin
Хороший пример, имеет смысл, спасибо!
(Есть некоторые «стилистичекие» замечания к авторам _такого_ формата, но раз уж выросло такое...)
При десериализации такого в объекты я бы в нескольких местах пропускал лишние промежуточные конверты (например, smev:MessageData/smev:AppData, или AddressData/Address), но наверное я чего-то не понимаю, и они там действительно нужны.
В-любом случае, я пока не вижу как, даже при таком адовом дизайне схемы, мы упрёмся в 32 вложенных подындекса. Все же хотелось бы примера из жизни, который имел бы смысл.
P.S.
И да, для больших BLOB-ов всегда и везде придется разбивать блоки на подузлы. В этом нет ничего специального — везде есть верхний предел блока базы, или страницы в хранилище, или ограничения по длине примитива, которые приходится обходить разбивая на куски, например, по 32КБ.
Трюк, при работе с базами данных, минимизировать количество обращений к диску, при заданных параметрах файловой системы и дискового хранилища. Понятно, что ни в какой ОС, нет возможности прочитать 32МБ BLOB за одно обращение к IO. И, понятно, что на практике придется читать блоками определенного размера (приближенными, например, к размеру блока файловой-системы). Так вот, при большом количестве возможных вариантов оказывается что хранение BLOB-ов кусками по 32КБ вполне хороший вариант. :)
lair
Это бесполезно. Спорить с госуровнем — гиблое дело. Я пробовал, не помогло.
Иногда нужны, если вам нужен полный аудит.
Ну, от этого конкретного примера до предела три уровня осталось. К сожалению, я не могу взять реальные данные из обмена, где было и побольше, пришлось публично доступные искать.
Ключевой вопрос — кому придется. Лично я предпочитаю, чтобы это за меня делала БД (включая оптимизацию этого доступа и прочее счастье). То есть я понимаю, что у БД есть страницы, а у хранилища ниже уровнем — блоки, но можно я об этом не буду думать?
tsafin
Так в Cache' и не надо об этом думать: поточные поля (символьные или бинарные) автоматически при сохранении бьются на куски.
Но в этом случае мы уже оперируем не чистым иерархическим хранилищем/глобалами, описанными автором, а пользуемся надстройкой над ними в виде объектов/классов, предоставленными Cache', но в основе своей использующими те же самые глобалы.
lair
Автор-то про глобалы и их применимость рассказывает, понятно, что поверх можно фигачить разные абстракции.
lair
На самом деле, в xml мы заодно влетаем в соседнее ограничение: максимальная длина ключа в 511 байт. Предположим, что мы добрые, и у нас однобайтовая кодировка (на самом деле, конечно, нет; ненавижу тебя, госуха) — это все равно 511 символов, а по-хорошему, имена надо хранить квалифицированными (я подозреваю, что если пример выше квалифицировать — то он уже вылетит за длину).
tsafin
Со СМЭВом этим вы заинтриговали, коллега, который этим занимается сейчас в отпуске, но у меня было впечатление что эта адовая XML схема вообще без проблем использовалась ими в их инсталляциях HealthShare/Ensemble в России. Вернется из отпуска — посмотрю на то как это проецируется в Cache'. Интересно.
inetstar
А у некоторых БД на глобалах — 1019 байт. У GT.M.
Чтобы упереться даже в 511, нужно чтобы каждый индекс глобала был в среднем больше 16.5 байт.
Глобалы — это специфический инструмент, весьма универсальный, но со своими ограничениями.
lair
Угу. Вот и я про то, что специфический.
inetstar
Вообще я не говорил, что глобалы самый лучший способ хранения XML. Это один из способов, когда приемлемая вложенность и нужен быстрый доступ к узлам.
lair
Вы говорили, что xml легко хранится в глобалах. Легко-то легко, только есть ограничения. Специализированные хранилища справляются с этим сущееественно лучше.
inetstar
Что же это вы исключений не обозначили? Вы утверждаете что специализированные хранилища справляются с этим существенно лучше в абсолютно всех случаях?
lair
Нет, зачем же во всех? В общих.
inetstar
Вообще расплывчатое определение. Общий случай.
Что это такое общий случай?
Если нужно хранить документы целиком, то я не удивлюсь, если кто-нибудь выберет глобалы из-за их скорости.
А если нужно менять/добавлять/удалять отдельные узлы в документах, то глобалы прямо созданы для этого.
Есть бесконечное множество сценариев использования иерархических документов. И для приличной части этого бесконечного количества могут подойти глобалы. Всё зависит от задачи.
lair
… ограничение в 3.6Мб.
… ограничение по глубине вложенности.
Нет, несомненно, глобалы могут подойти. Просто каждый раз есть вопрос — а нет ли чего-то, что подойдет лучше?
inetstar
Ограничения есть у всех баз. Если хранить документы целиком, то 16МБ, на весь документ, очень жёсткое ограничение.
А если нужно декомпозировать на иерархическую структуру с быстрым доступом к свойствам, то 3.6Мб на отдельное свойство не такое и большое ограничение.
Технологию подбирают подзадачу. Я постарался систематизировать сильные стороны глобалов.
lair
Ну так возьмите БД, в которой нет такого ограничения. Это не обязательно глобалы.
… пока мы не пытаемся хранить в свойствах файлы.
Ну вот XML, файлы и индексы — как-то не сильная сторона оказалась.
inetstar
Сильная, только для специфических случаев. Каких — уже говорил.
Специализированная БД для файлов — это файловая система.
inetstar
16МБ — это у Mongo. У подобных Mongo баз есть ограничения на объём документа, весьма жёсткие. И на число уровней (у Mongo — 100). Так как иначе парсер (при доступе к конкретным свойствам) будет вешать сервер.
lair
Ага. Вот например у RavenDB нет (точнее, есть — около пары гигабайт).
Конечно, можно сказать, что равен не подобен монге (и в чем-то это правда даже), но равен — честная ДОДБ.
vedenin1980
Сказки, у монго доступ к конкретным свойствам очень быстрый. И между 16Мб и 3,6 Мб и 100 и 31 уровней вложенности — четыре большие разницы.
inetstar
По сравнению с чем? Сколько выборок в секунду ты можешь сделать из разных документов объёмом 3МБ с 15-го уровня вложенности?
Тут есть чёткие дихотомии.
Упрощённый алгоритм для выбора иерархической документно-ориентированной базы с возможностью быстрого доступа к свойствам.
0) Документы больше 16МБ
— но свойства меньше 3.6 — глобалы
— свойства не укладываются в 16МБ — не то, ни другое (если не использовать разрезание документов на части)
2) Документы целиком больше 3.6 и меньше 16 — Mongo
3) Меньше 3.6МБ и больше 100 уровней вложенности — Mongo
4) Меньше 3.6МБ и менее 31 уровня вложенности — глобалы
5) Документы около 1К — на вкус и цвет товарищей нет
lair
Вы так говорите, как будто кроме монги и глобалов жизни не существует.
Не говоря уже о том, что задача («выбор иерархической документно-ориентированной базы с возможностью быстрого доступа к свойствам») изначально неверна, плясать надо от сценариев использования. Ну и да, polyglot persistence рулит.
inetstar
vedenin1980 — специалист по Mongo, я пишу про глобалы, и с ним мы обсуждали именно Mongo vs глобалы.
А вот за вами я заметил сомнительную привычку влезать в чужие диалоги. Вы делаете это постоянно. Из-за чего в пылу дискуссии разок я вас уже перепутал.
lair
Ну и пишите тогда «алгоритм по выбору между монгой и глобалами».
(а вообще, конечно, диалоги — это в личной переписке, а здесь публичное обсуждение, все-таки)
4dmonster
Скажите, а для документа выше как надо поступить?
Какая структура для его компонентного хранения?
Мне как не специалисту кажется, что требуется аналог Parent_ID и/или Child_ID а не пытаться уровни иерархии документа вписать в уровни иерархии индексов.
inetstar
Вообще, как я понял, приведённый документ, это некое сообщение. То есть вещь, которую нужно доставить из А в Б.
Я бы хранил его либо целиком, либо разбив на большие куски, чтобы у меня был сразу доступ к нужному контейнеру, без разпарсивания.
vedenin1980
Максимальная вложенность исходного кода этой страницы как раз 20. Она монстр?
inetstar
А есть хоть какой-то смысл хранить HTML этой страницы в БД с обеспечением быстрого доступа к каждому узлу?
Подобные документы достаются из БД целиком.
А вот например в XML медкарты добавить новый узел со сканом МРТ будет удобно.
4dmonster
Самое очевидное — если её надо сформировать.
inetstar
Формировать страницу через то, что записать всё по тэгам в иерархическую БД?
Это будет медленнее, чем формировать напрямую в памяти.
Использовать глобалы как кеш? В этом случае имеет смысл хранить куски документа целиком без разбиения по свойствам.
4dmonster
Прямо на М и формировать. Данные в глобалах, мета-данные тоже, пусть М программы и формируют страничку.
Можно использовать глобалы во время отладки.
А в продакшене — локальные переменные.
inetstar
Как я понимаю, на Cache Server Pages так и делают.
Вообще я удивлён, что M в своё время не захватил нишу веб-разработки как PHP. Языки-то сильно похожие.
lair
Ага, скан у вас помещается в 3.6Мб? Мне когда последний раз выдавали на руки сканы (не МРТ правда, а рентгена) — они как раз заняли половину CD-болванки (какой-то чертов пропьетарный формат для медицинского ПО). Да что там, у меня в 3.6 мегабайта фотографии с моего фотоаппарата не помещаются.
inetstar
Болванка МРТ состоит из множества файлов в формате DICOM. Как кино из отдельных кадров.
Я открыл свою МРТ — каждый кадр это 586КБ.
Тем более, я не уверен, что есть смысл в медкарте хранить все эти файлы. Достаточно выбрать самый характерный.
Но таких отдельных кадров, сканов анализов может быть очень много.
lair
К сожалению, то, что отдельный скан может быть больше 3.6Мб — это такая реальность, данная нам в ощущениях. Не МРТ — так рентген, не рентген — так еще что-то.
Нет, я понимаю, что объяснить заказчику можно любое ограничение (чего уж скрывать, все мы так рано или поздно делали), но на этом фоне придирки к аналогичным ограничениям у монги выглядят мелко.
inetstar
Поэтому эти придирки и в спойлере. И там жёстко обозначена область, в которой глобалы подходят лучше.
Структуры хранения в Mongo и глобалы логически идеентичны. Различия только в реализациях и во встроенных языках.
Если интересно, то Mongo хочет влезть на этот рынок и готовит презентации, где говорит, что Mongo — это почти глобалы)
lair
Угу. Та, где вам, на самом деле, не нужно хранилище документов, а нужно хранилище свойств. Дальше вопрос к сценариям использования и архитектуре приложения.
inetstar
Документы состоят из свойств. Глобалы дают возможность красиво и быстро декомпозировать документы по свойствам.
Это и есть ключевое преимущество перед БД, которые хранят целиком.
Глобалы — это одна из самых первых документно-ориентированных баз, если не первая вообще (66г!).
lair
Это неоднозначный вопрос, сводящийся к тому, состоит ли целое из частей. Смысл ДОДБ, обычно, все-таки в том, чтобы работать с документом, как с целым (я приводил сценарий использования, он не единственный).
inetstar
В любом случае: глобалы позволяют как хранить документ целиком, так и разбивая на части. С известными ограничениями.
По логической структуре глобалы — это надмножество над документно-ориентированными базами, которые хранят документы целиком.
lair
Не-а. Глобалы — надмножество над key-value, потому что глобалы не умеют анализ содержимого документа (а именно оно специфично для документо-ориентированных хранилищ).
4dmonster
А может Глобалы — key-value, где key может быть массивом?
inetstar
Да. Почему нет. Следующая статья будет об этом.
lair
Эта идея приходила мне в голову, но она, мне кажется, все-таки слишком упрощает ситуацию: групповые удаления, обходы — это все не ложится в семантику композитного ключа.
inetstar
Согласен. Глобалы — иерархическое хранилище, с интерфейсом доступа к конкретным узлам как у многомерного массива. Я бы так определил.
inetstar
Расскажите мне как анализирует, например, MongoDB документы.
Желательно как оно это делает само. Без запросов со стороны.
Так как к любой БД на глобалах также можно написать интерфейс для запросов. И код для анализа.
lair
Ну как-как, вы скармливаете СУБД документ (кому что, в Mongo — JSON/BSON, RavenDB — .net-объект), та его сохраняет в понятном для себя виде, и в случае необходимости (например, при индексации или запросе) парсит содержимое.
vedenin1980
Это лишь простейший пример, на самом деле зачастую в энтерпрайзе вообще требуется бесконечное кол-во уровней иерархии, например вы хотите сделать сервис который строит дерево всех страниц любого сайта инета, доступных с главной страницы сайта, естественно вы должны считать что вложенность тут может быть бесконечной (ну или заведомо очень большой). И таких случаев в реальном энтерпрайзе довольно много.
tsafin
В данном случае Вы строите не дерево а _граф_
anjensan
Как и первая статья — много маркетологической шелухи, никакой конкретики.
Тут что-то про скорость говорили… Так а где повторяемые и открытые тесты?
Где техническое описание, как же оно работает «под капотом»?
Напоминаю, что это хабр, а вовсе не гиктаймс или мегамозг.
inetstar
Цель этой статьи — обозначить области применения глобалов, поскольку многие вообще не знают об этой технологии и о её сильных сторонах.
На техническое описание формата хранения я дал ссылку в первых же комментах.
По реализации — уже говорилось, что известные БД на глобалах работают на B*-tree, но в принципе могут работать и на других алгоритмах.
Не все читатели тут являются разработчиками БД. Моя цель в этих статьях — показать для чего нужны глобалы, а не как конкретно они работают и какие оптимизации были использованы.
tsafin
Давайте, все же я попытаюсь вернуть дискуссию в правильное с методической точки зрения русло?
Моя позиция такова — с логической точки зрения разницы многомерного хранилища (глобалов), документной базы (JSON/BSON) и XML иерархии нет. Они все оперируют эквивалентным множеством моделей и данных.
(Что я попытался донести в своей презентации на RIT 2015 Cache'Conf —
www.slideshare.net/TimurSafin/multimodel-database-cach )
Интересна позиция Mongo в этом отношении osehra.org/sites/default/files/NoSQLtheModernMUMPS.pdf
При разговоре с группой пользователей VistA они пытались донести до аудитории мысль, что Mongo/документные базы, это тоже самое, что и иерархические базы (MUMPS/Cache'). С чем я не могу не согласиться :)
Еще один важный аспект, в большинстве случае движки СУБД оперируют вариациями b-tree формата (когда они не SSTable/LSM, конечно). И Cache' с его b*-tree тут не сильно отличается, хотя и отличается в деталях, облегчающих множество операций с глобалами, упомянутых Сергеем inetstar в этих двух статьях. Детали формата b*-tree мы попытались раскрыть в двух презентациях на недавнем RIT (моей ретроспективной, упомянутой выше, и Сергея Шутова про внутренности собственно Cache' b*-tree. Как оказалось его презентации в онлайне нет, как только исправим ситуацию сообщу в эту ветку)
BalinTomsk
— не тратя при этом лишнее дисковое пространство на пустые колонки, как было бы в реляционном случае.
вы когда делаете такие заявления хотя бы подкрепляте фактами. Например: MSSQL 2014
SELECT t.NAME AS TableName, p.rows AS RowCounts, SUM(a.used_pages) * 8 AS UsedSpaceKB, (SUM(a.total_pages) — SUM(a.used_pages)) * 8 AS UnusedSpaceKB
FROM sys.tables t INNER JOIN sys.indexes i ON t.OBJECT_ID = i.object_id INNER JOIN sys.partitions p ON i.object_id = p.OBJECT_ID AND i.index_id = p.index_id
INNER JOIN sys.allocation_units a ON p.partition_id = a.container_id LEFT OUTER JOIN sys.schemas s ON t.schema_id = s.schema_id
WHERE t.NAME LIKE 'slnx_job' AND t.is_ms_shipped = 0 AND i.OBJECT_ID > 255 GROUP BY t.Name, s.Name, p.Rows ORDER BY UsedSpaceKB DESC
table | rows | used space | unused space
slnx_job | 1626829 | 1019848 | 392
Вставляю 10 колонок — размер не изменился.
alter table slnx_job add c1 int
alter table slnx_job add c2 int
alter table slnx_job add c3 int
alter table slnx_job add c4 int
alter table slnx_job add c5 int
alter table slnx_job add c6 int
alter table slnx_job add c7 int
alter table slnx_job add c8 int
alter table slnx_job add c9 int
Уверяю вас даже после добавления 10,000 колонок размер не поменяется. Реляционные базы не хранят пустые пространства.
Насчет ХМЛ. Например DB2 хранит XML как внутри набор настояших реляционых таблиц со всей соотвествуюшей скоростью доступа.
inetstar
Это потому что вы вставили колонки без значений по-умолчанию.
Попробуйте задать такие значения.
Далее, для корректности вашего теста, вам нужно было определить в каждой строке случайным образом один аттрибут и посмотреть на какой размер изменилась таблица.
P.S. Я тестировал на MySQL 5.5.
Ждёт результатов тестирований. Надеюсь, таблица slnx_job у вас большая.
lair
А зачем использовать значения по умолчанию в БД? Точнее, не так: значение по умолчанию — это не пустая колонка, это именно имеющее смысл значение (например, текущая дата). И неудивительно, что на него тратится место. А на несуществующие значения — не тратится (точнее, зависит от реализации).
inetstar
Во многих БД таблицы, где NULL не используются работают существенно быстрее.
lair
У вас, конечно же, есть тесты, подтверждающие это заявление. Скажем, в MS SQL я такого никогда не наблюдал (и, собственно, не понимаю, откуда там этому логически взяться).
inetstar
For best performance, and to reduce potential code bugs, columns should ideally be set to NOT NULL. For example, use of the IS NULL keywords in the WHERE clause makes that portion of the query non-sargeble, which means that portion of the query cannot make good use an index.
И подобных статей много. Ещё одна:
lair
Еще раз спрашиваю, тесты есть?
По мелочам:
NULL
(обратите внимание, не просто по колонке, которая может содержать такие значения, а именно поиск строк содержащих такие значения) индексы не используются, т.е. стоимость такого поиска (и того такого, а не любого другого) эквивалентна полному перебору. Во-первых, она ничего не говорит о любых других операциях по такой таблице. Во-вторых, просветите меня, пожалуйста, какова алгоритмическая сложность поиска узла, не содержащего заданный подиндекс, в глобале? Т.е., как найти всех пользователей, у которых не задан город?inetstar
А в глобалах я легко могу сделать подобный индекс. В индексном глобале (и только в нём), например, в качестве NULL буду использовать "\0"
lair
В RDBMS тоже можно сделать подобный индекс, технически ничего не мешает.
(кстати, а что вы будете делать, когда \0 — валидное значение?)
lair
Собственно, постгре и индексирует: «Also, an
IS NULL
orIS NOT NULL
condition on an index column can be used with a B-tree index.»lair
Даже больше, есть мнение, что
NULL
не только не замедляют, но и ускоряют работу постгре.smagen
NOT NULL, в некоторых случаях, действительно ускоряет работу постгреса. Если в таблице в начале идут NOT NULL атрибуты фиксированной длины, то доступ к этим атрибутам осуществляется по предвычисленным значениям. Однако, почувствовать это преимущество на паре десятков атрибутов, вряд ли получится.
P.S. Умоляю вас, «постгрес» :)
inetstar
Разговор был о MS SQL.
Условный индекс — это не то. Придётся для каждого столбца делать 2 индекса — по колонкам и условия. Это убьёт производительность. А в моём случае это будет один индексный глобал на одну колонку (set колонок).
Напоминаю исходную точку разговора: не рекомендуется вручную программировать таблицы на глобалах. Не к тому цепляетесь.
lair
Разговор был о «во многих БД». Впрочем, вы и в MS SQL падение производительности не подтвердили.
А я и не про условные индексы.
С чего вы это взяли?
Я вообще не понимаю, зачем программировать таблицы на глобалах, когда есть нормальные табличные инструменты.
lair
… который тоже поддерживает индексы с
NULL
.Передаю пламенный привет (устаревшим) статьям, которые вы цитировали.
inetstar
1) Вы не показали определение таблицы, в котором это поле указано как индексируемое
2) Приведите ссылку на документацию
3) И что это действительно не только для текстовых полей.
4) Сделайте ещё поиск по NOT NULL по текстовому и не по текстовому полю
5) Вы не сможете сделать строку из 10 000 столбцов, размером более 8КБ. Так что глобалы на разреженных данных MS SQL в общем случае заменить не сможет.
lair
Вы серьезно считаете, что MS SQL способен использовать индекс, который не задан? Но мне не сложно.
msdn.microsoft.com/en-us/library/ms188783%28v=sql.110%29.aspx
Чтобы сохранить разреженные данные, не обязательно пихать их в одну строчку одной таблицы. Поэтому как раз в общем случае — сможет.
inetstar
1) Вообще-то в документации ничего не говорится об ускорении выборок строк с NULL-значениями.
2) И я просил привести примеры с NOT NULL ещё. Я-то легко на глобалах сделаю индексированную выборку по NOT NULL. О чём вы как-то забыли. Приводите.
3) Таблица в общем случае никогда не была деревом. Сэмулировать дерево на таблицах можно, но это нерационально. В этом тоже в статье написано.
lair
А должно? Но вот вам: msdn.microsoft.com/en-us/library/ms190457%28v=sql.110%29.aspx
Никаких специальных оговорок про NULL нет.
А вы меньше редактируйте комментарий после отправки, меньше будет «забываться».
Не более нерационально, чем имитировать таблицы на дереве. Но речь-то не об этом, речь о том, чтобы сохранить конкретные данные в реляционной структуре, а это совсем не то же самое, что «сэмулировать дерево».
BalinTomsk
1. Для это есть специальный индексный сервер.
2.https://msdn.microsoft.com/en-us/library/ms142571.aspx
3.
create table AAA
(
id int not null primary key,
c1 int null index c1_index,
c2 float null index c2_index
)
5)идея хранить дерево всего в одной таблице — неверная с архитектурной точки зрения вреляционых базах и мне видится неверной точка зрения что нельзя сохранить медицинскую историю человека в реляционной структуре.
Число людей, болезней, симптомов, процедур, снимков, мнений, да чего угодно настолько конечно, что даже SAP с 36000 таблиц кажется монстром, по сравнению базой хранения всего типичного госпиталя, которую вы считаете нельзя положить в реляционную таблицу.
Watson использует DB2 и им в голову не пришло использовать нереляционную базу. Вы же не будете отрицать что ничего близко похожего на ваших глобалах не сделано.
Если вам нужно более чем 10000 или миллион колонок — это не проблема сделать нужное количество таблиц с отношением 1:1
inetstar
Я просто попросил показать определение таблицы
Требовалась ссылка на документацию, в которой было бы явно написано, что поля с NULL-значениями индексируются.
Можно использовать несколько таблиц. И это всё равно будет ошибка с точки зрения архитектуры.
достоверной информации о том, что Watson использует DB2 как единственное хранилище не нашёл. Однако у IBM есть технология аналогичная глобалам.
Самая большая медицинская система с самым большим числом пользователей VistA, что говорит в пользу глобалов и об их подходящести для данной сферы. Собственно говоря, глобалы именно для этой сферы и придумали.
inetstar
Статья откуда цитата про «best perfomance»
Нам лучше вернуться к теме. Так как больше 1024 колонок вы не сделаете в MS SQL. А в спец. типе таблиц wide строка не более 8КБ.
lair
Это ровно тот же аргумент, что уже обсуждался — про индексы и поиск с IS NULL. Не интересно по второму разу обсуждать.
А надо?
Я вам честно скажу, это ограничение есть во всех таблицах MS SQL, не только в wide. Правда, начиная с 2008-го, есть row overflow для строковых и бинарних типов, но он подтормаживает.
inetstar
В статье была фраза, что глобалы хороши, когда есть много необязательных свойств, например в медкартах, а на таблицах тоже самое было реализовано десятками тысяч столбцов.
Человек мне сказал, что разреженные столбцы не занимают место и похвастался, что мог бы вставить 10 000 колонок в MS SQL. Так вот. Не смог бы. А если и смог бы, то там свои ограничения.
8КБ на строку с пациентом — это абсолютно нереально. И там ещё нельзя по одной новые колонки добавлять в этом типе таблиц.
lair
Во-первых, для медкарт лучше ДОДБ. Но, во-вторых, если бы заказчик настаивал на RDBMS, то немного разумной декомпозиции — и все будет хорошо.
Я ни разу в жизни не видел ни одного объекта, у которого бы реально было хотя бы несколько сотен индивидуальных, недекомпонуемых на группы свойств. Человеческий мозг так не работает, ему некомфортно в таких моделях.
inetstar
Базы на глобалах — это и есть ДОБД.
А вот я видел. Даже 40 свойств с десятками подсвойств заставляют разработчиков уже сходить с ума.
Я встречал это в базе, где хранилась персональная информация о людям.
Пришлось использовать EAV — я тогда ещё не знал о глобалах.
lair
Это не единственные существующие ДОДБ. Более того, я уже писал, почему я не считаю глобалы ДОДБ.
Извините, но это не «сотни свойств», это именно «40 свойств с десятками подсвойств». У вас уже есть декомпозиция, но вы ее почему-то не видите.
inetstar
Вы просто не разбираетесь в ДОБД.
Почитайте википедию.
Там есть MUMPS.
Я вижу многоуровневое дерево. Ясно и отчётчиво.
На таблицах это либо EOV или 160 таблиц (с декомпозицией). А на глобалах — одно красивое дерево.
lair
Давайте не будем доставать и меряться, кто в чем разбирается, хорошо?
...eye of the beholder. Вы привыкли так декомпоновать системы, потому что вы привыкли так работать с глобалами. Это не значит, что это единственная возможная декомпозиция этой системы.
Откуда взялась цифра «160»? Вы посчитали сущности в E-R?
inetstar
Более скромно было сказать: Извиняюсь, вы правы.
Википедия — очень хороший аргумент. В мою пользу. Там за одну фразу может многомесячная борьба идти.
Вы сами себе выдумали определение ДОБД и решили, что оно самое лучшее. Так что ваше определение ДОБД — субъективное, а не то, которое реально используется в мире. А MUMPS — это ДОБД.
Это примерная цифра. Слава богу, хватило ума, не делать полную нормализацию, а использовать EAV.
40 необязательных свойств, у которых у каждого может быть начиная от 4-х вложенных свойств. У которых также могут быть вложенные свойства. Все эти свойства разнотипны, а не просто текстовые поля определённой длины.
Так что при полной нормализации без EAV будет, как минимум, 1 таблица 40 колонками для свойств + 160 таблиц связей с подсвойствами + 4*40 таблиц подсвойствами = 321 даже. И это только при двухкратном уровне вложенности.
А на EAV 4 таблицы (из-за неограниченных вложенностей, а так вообще 3).
Но меня всё время не оставляла мысль, что это решение (EAV) не идеальное. Когда я узнал о глобалах, то понял то, что они то что нужно.
lair
Субъективное, говорите?
(Pramod J. Sadalage, Martin Fowler, NoSQL Distilled: A Brief Guide to the Emerging World of Polyglot Persistence)
Я же говорю, не надо доставать и меряться. На поле с неустойчивой терминологией это заведомо бесполезное действие.
Вы мыслите свойствами, а не сущностями. Это опасный подход (хотя я и понимаю, откуда он у вас растет). Повторюсь, есть отличная от нуля вероятность, что существует иная декомпоновка этой модели, позволяющая получить более разумную структуру БД. К сожалению, дать более разумных комментариев, не видя модели, я не могу.
Ограничусь одним примером. Возьмем одну из ваших структур данных:
Если бы я это моделировал в ER, то единственными свойствами здесь оказались бы INN, Name и City. Все прочее декомпонуется в связи и сущности (к сожалению, я не вижу, какие семантические атрибуты есть на следующем уровне, и, как следствие, во что их надо отразить).
BalinTomsk
----Это потому что вы вставили колонки без значений по-умолчанию.
— Попробуйте задать такие значения.
A зачем? мы все еше дерево пытаемся уместить — или вы на ходу решили подменить условие задачи?
---Далее, для корректности вашего теста, вам нужно было определить в каждой строке случайным образом один аттрибут и посмотреть на какой размер изменилась таблица.
DECLARE @intFlag INT
SET @intFlag = 1
WHILE ( @intFlag <= 900)
BEGIN
declare @cn sysname = N'k' + CAST( @intFlag as sysname);
declare script sysname = N'alter table slnx_job add ' + @cn + N' int'
EXECUTE sp_executesql script
SET @intFlag = @intFlag + 1
END
GO
3 раза проапдейтил случайное значение в каждую из колонок — в целом то слегка больше то меньше — это связано с тем что данные хранятся в 8К страницах. Ничего ненужного MSSQL не хранит.
1,626,829 rows
inetstar
Хорошо, что вы решили провести тесты.
Однако я не вижу в вашей процедуре вставок значений. Вы просто добавили 900 пустых колонок.
И не вставили ни одного реального значения.
Данные должны быть разреженными, т.е. в некоторых колонках должны быть значения.
В вашем случае просто изменилось описание таблицы, а не её содержимое.
В некоторых БД оно вообще в отдельном файле хранится.
А лучше 1-3 случайных аттрибута.
Я уже писал про ограничение на 8КБ строку в MS SQL в wide типе — это очень мало для документа.
А в обычную таблицу вы 9000 колонок не добавите.
Ещё нужно было размер таблицы в байтах указывать.
BalinTomsk
Вы внимательно читали что я ответил? 3 раза проапдейтил случайное значение в каждую из колонок
DECLARE @intFlag INT, @cnt int = 0
SET @intFlag = 1
WHILE ( @intFlag <= 900)
BEGIN
declare @cn sysname = N'k' + CAST( @intFlag as sysname);
declare val sysname = CAST(CAST(1000*RAND(CHECKSUM(NEWID())) AS int) as sysname);
declare script sysname = N'update slnx_job set ' + @cn + N' =' + val + N' WHERE slnxjob_id = ' + val
EXECUTE sp_executesql script
SET @cnt = @cnt + @@ROWCOUNT
SET @intFlag = @intFlag + 1
END
select @cnt
GO
Было 291272 стало 294992
Было 294992 стало 299052
— А в обычную таблицу вы 9000 колонок не добавите.
А надо? Добавление очередных 10,000 (аля колонок) атрибутов решается добавление поля с типом атрибута и значением 1-10000.
Если хотите хранить честное дерево то это спасет отца демократии:
https://msdn.microsoft.com/en-us/library/bb677173.aspx
inetstar
Я вижу проблему таблица с 1,626,829 rows и N+900 колонок занимает у вас 291272 байт.
Т.е. 0.17 байт на строку. Вы явно в чём-то ошиблись.
Даже если бы вы в каждую строку вставили по одному int — 4 байта, то размер был бы у вас не менее 6 000 000 байт, а у вас всего 290КБ.
Т.е. явно в чём-то ошибка. Объяснить это рационально предлагаю вам.
inetstar
Это нечестное дерево, так как оно предполагает одинаковую структуру полей для всех узлов, на всех уровнях.
Но я соглашусь, что это хорошее подспорье в некоторых случаях.
inetstar
То есть нужен цикл по всем id, со вставкой в случайную колонку.
Хранимку придётся писать. Ну или на ASP/PHP.
И ещё. Не знаю как MS SQL, но в MySQL с таблицы с колонками NULL работают медленнее.
Поэтому я, например, их избегаю.
inetstar
Я могу такое допустить только в случае, когда абсолютно все эти колонки пустые.
Если же вы начнёте их хоть чуть-чуть использовать, то БД сразу начнёт выделять, как минимум, 1 бит на колонку.
В том тесте, что я предложил, если одна неточность. Вставлять нужно не одно значение в случайную колонку, так база может увеличить все строки на 4 байта, а случайное число значений (допустим 1-3) в случайные колонки каждой строки, вот тогда тест будет более менее точным.
inetstar
А вот тут вы очень глубоко ошибаетесь. В MS SQL максимум 1024 колонки.
Исключение: есть специальный тип таблиц — wide — там до 30 000 колонок. Но в этом типе строка не больше 8КБ. Что сразу ставит крест на многих видах применений.
Второй минус этого типа — The column set cannot be changed. To change a column set, you must delete and re-create the column set.
inetstar
И репликацию использовать нельзя для таких таблиц. Остальные детали
lair
Но:
Так все-таки, как соотносятся по скорости удаление поддерева и удаление одиночного узла в глобале?
inetstar
Это вы нашли что-то типа опечатки. Сейчас исправлю.
Устанавливать придётся дольше, конечно, чем удалять.
Групповое удаление работает по моим тестам в 260-455 раз быстрее, чем индивидуальное удаление узлов. Для двухярусного дерева.
lair
Я спрашивал об обратной ситуации: как соотносится удаление одного дочернего узла с удалением дочернего поддерева?
inetstar
В зависимости от числа элементов. Скорость удаления поддерева зависит от числа узлов. Ориентировочно, если в поддереве 520 узлов, то его удаление будет в 2 раза дольше, чем одиночного узла. Я тестировал удаление поддерева со 50M элементами.