Глобалы — мечи-кладенцы для хранения данных. Деревья. Часть 1

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

    Если использовать глобалы в решении тех задач, в которых они действительно хороши, то можно добиться выдающихся результатов. Либо в производительности, либо в упрощении решения задачи (1, 2).

    Глобалы — это специальный способ хранения и обработки данных, совершенно другой, чем таблицы в SQL. Они появились в 1966 году в языке M(UMPS) (эволюционное развитие — Caché ObjectScript, далее COS) в медицинских БД и до сих пор там активно используются, а также проникли в некоторые другие области, где требуется надёжность и высокая производительность: финансы, трейдинг и т.д.

    Глобалы в современных СУБД поддерживают транзакции, журналирование, репликацию, партиционирование. Т.е. на них можно строить современные, надёжные, распределённые и быстрые системы.

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

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

    2. Как работают глобалы


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

    Упрощённого говоря, глобал — это персистентный массив. Массив, который автоматически сохраняется на диск.
    Трудно представить что-то более простое для хранения данных. В коде (на языках COS/M) от обычного ассоциативного массива он отличается только символом ^ перед именем.

    Для сохранения данных в глобале не нужно изучать язык запросов SQL, команды для работы с ними очень просты. Их можно выучить за час.

    Начнём с самого простого примера. Одноуровневое дерево с 2-мя ветвями. Примеры написаны на COS.

    Set ^a("+7926X") = "John Sidorov"
    Set ^a("+7916Y") = "Sergey Smith"

    При вставке информации в глобал (комaнда Set) автоматически происходят 3 вещи:

    1. Сохранение данных на диск.

    2. Индексация. То что в скобках выступает ключом (в англоязычной литературе — «subscript»), а справа от равно — значением («node value»).

    3. Сортировка. Данные сортируются по ключу. В дальнейшем при обходе массива первым элементом станет «Sergey Smith», а вторым «John Sidorov». При получении списка пользователей из глобала база не тратит времени на сортировку. Причём можно запросить вывод отсортированного списка, начиная с любого ключа, даже несуществующего (вывод начнётся с первого реального ключа, который идёт за несуществующим).

    Все эти операции происходят невероятно быстро. На домашнем компьютере я получал значения до 750 000 вставок/сек в одном процессе. На многоядерных процессорах значения могут достигать десятков миллионов вставок/сек.

    Конечно, сама по себе скорость вставки мало о чём говорит. Можно, например, очень быстро записывать информацию в текстовые файлы — так по слухам работает процессинг Visa. Но в случае глобалов мы получаем на выходе структуризованное проиндексированное хранилище, с которым можно в дальнейшем просто и быстро работать.

    • Самая сильная сторона глобалов — это скорость вставки новых узлов.

    • Данные в глобале всегда проиндексированы. Их обход как на одном уровне, так и вглубь дерева, всегда быстр.

    Добавим в глобал ещё несколько ветвей второго и третьего уровня.

    Set ^a("+7926X", "city") = "Moscow"
    Set ^a("+7926X", "city", "street") = "Req Square"
    Set ^a("+7926X", "age") = 25
    Set ^a("+7916Y", "city") = "London"
    Set ^a("+7916Y", "city", "street") = "Baker Street"
    Set ^a("+7916Y", "age") = 36



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

    Как видно информацию можно хранить как в ключе, так и значении. Общая длина ключа (сумма длин всех индексов) может достигать 511 байт, а значения 3.6 МБ для Caché. Число уровней в дереве (число измерений) — 31.

    Ещё интересный момент. Можно построить дерево, не задавая значений узлов верхний уровней.

    Set ^b("a", "b", "c", "d") = 1
    Set ^b("a", "b", "c", "e") = 2
    Set ^b("a", "b", "f", "g") = 3

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

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



    Как мы видим у садовых деревьев листья и плоды находятся только на концах ветвей.
    Файловые системы — информация хранится только на концах ветвей, которые являются полными именами файлов.

    А вот структура данных глобала.

    Отличия:

    1. Внутренние узлы: информация в глобале может храниться в каждом узле, а не только на концах ветвей.
    2. Внешние узлы: у глобала обязательно должны быть определены значения на концах ветвей, у деревьев ФС и садовых — нет.

    В части внутренних узлов, можно сказать, что структура глобала является надмножеством структуры деревьев имён в файловых системах и садовых деревьев. Т.е. более гибкой.

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

    Чтобы лучше понять работу глобалов представим, что было бы, если создатели файловых систем использовали для хранения информации подход аналогичный глобалам?

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

    2. Надобность в директориях бы отпала. Просто были бы файлы с подфайлами и файлы без подфайлов. Если сравнить с обычным деревом, то каждая ветвь стала бы плодом.



    3. Такие вещи как файлы README.txt, возможно, отпали бы. Всё что нужно было сказать о содержимом директории можно было записать в сам файл директории. В пространстве путей имя файла неотличимо от имени директории, поэтому можно было обойтись одними файлами.

    4. Скорость удаления директорий с вложенными поддиректориям и файлами резко увеличилась бы. Много раз на хабре проскакивали статьи о том, как долго и трудно удалять миллионы мелких файлов (1, 2). Однако, если сделать псевдофайловую систему на глобале, то это будет занимать секунды или их доли. Когда я тестировал удаление поддеревьев на домашнем компьютере, то за 1 секунду удалялось 96-341 миллионов узлов из двухярусного дерева на HDD (не SSD). Причём речь идёт об удалении части дерева, а не просто всего файла с глобалами.


    Удаление поддеревьев — ещё одна сильная сторона глобалов. Для этого не нужна рекурсия. Это происходит невероятно быстро.

    В нашем дереве это можно было бы сделать командой Kill.

    Kill ^a("+7926X")



    Для лучшего понимания того, какие действия нам доступны над глобалами, приведу краткую таблицу.
    Основные команды и функции по работе с глобалами в COS
    Set Установка ветвей до узла (если ещё неопределены) и значения узла
    Merge Копирование поддерева
    Kill Удаление поддерева
    ZKill Удаление значения конкретного узла. Поддерево выходящее из узла не трогается
    $Query Полный обход дерева с заходом вглубь
    $Order Обход веток конкретного узла
    $Data Проверка определён ли узел
    $Increment Атомарное инкрементирование значения узла. Чтобы не делать считывания и записи, для ACID. В последнее время рекомендуется менять на $Sequence
    Спасибо за внимание, готовы ответить на ваши вопросы.

    Disclaimer: данная статья и мои комментарии к ней является моим мнением и не имеют отношения к официальной позиции корпорации InterSystems.

    Продолжение Глобалы — мечи-кладенцы для хранения данных. Деревья. Часть 2. Вы узнаете какие типы данных можно отобразить на глобалах и на каких задачах они дают максимальный выигрыш.
    InterSystems
    InterSystems IRIS: СУБД, ESB, BI, Healthcare

    Комментарии 336

      +4
      Настоящие мечи-кладенцы баз данных — глобалы — давно известны, но до сих пор немногие умеют эффективно ими пользоваться или вовсе не владеют этим супероружием.

      Очень заинтересовали что за глобалы такие, начал искать в инете. Поправьте меня если я ошибаюсь но разве это не синоним обычного key-value или nosql хранилища? Учитывая популярность всяких nosql баз и хранилищ, мне кажется это давно уже не супероружие, а один из самых распостраненных инструментов наряду с sql базами данных. Или в идеи глобалов есть все-таки что-то ещё кроме key-value или nosql хранилища? Чем глобал отличается от обычного nosql хранилища, построенного на основе бинарного дерева? В каких nosql хранилищах используется эта концепция?
        +2
        Это надмножество key-value. В следующей статье об этом будет подробно.
        В 2-х словах — глобалы — это иерархический key-value. Где под одним key можно хранить целое дерево значений и ключей.

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

        Как правило к глобалам идёт специальный язык на котором можно писать хранимые процедуры (в терминах SQL) и полноценные программы.

        Программы на этом языке можно компилировать — и это даёт огромную скорость обработки данных в БД по сравнению с интерпретируемым SQL.
          +3
          Как правило глобалы работают со скоростью in-memory БД при этом обеспечивая хранение на диске, транзакции и другие плюшки.

          А можно поподробнее, как это достигается?
            0
            Когда я смотрел исходный код GT.M я был поражён — не менее 50% кода написано на ассемблере (причём под все архитектуры), а остальное на Си. Исходный код Caché закрыт, но я думаю там таже история. Они недавно писали, что оптимизируют всё даже под конкретные модели процессоров.
              +3
              Оптимизация на ассемблере — это хорошо, но скорость работы с диском имеет банальные физические ограничения (и поэтому заведомо медленнее работы с памятью). Как это ограничение обойдено?
                0
                Есть 3 вопроса в вашем: при вставке, обновлении, выборке.

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

                При случайной выборке из объёмов многократно превосходящих ОЗУ мы упираемся в IOPS диска (тестировал сам), но сама структура хранения глобалов такова, что дерево хранится очень компактно, и поэтому за один IOPS считывается кусок дерева.

                То же при обновлении: я подозреваю, что за 1 IOPS обновляется сразу несколько значений узлов.

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

                Также нужно учитывать, что современные in-memory БД появились недавно и поэтому не до конца оптимизированы. Авторы вот этой статьи утверждают, что GlobalsDB рвёт по тестам Redis. Можно у них попросить данные тестирований.

                Также, я думаю, если скидывать значения на диск раз в несколько секунд, то одних ассемблерных и алгоритмических оптимизаций будет достаточно, чтобы выигрывать у тех in-memory продуктов, которые полностью написаны на высокоуровневых языках.
                  +1
                  При случайной выборке из объёмов многократно превосходящих ОЗУ мы упираемся в IOPS диска (тестировал сам),

                  То есть скорость, внезапно, не от in-memory, а упирается в диск.

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

                  Буферизация? Т.е., пока буфер не наберется (ну или таймаут не случится), записи на диск не будет?
                    +1
                    Можно изложить вот так. Если мы тестируем in-memory БД и БД на глобалах на одинаковом объёме ОЗУ, то ничего не мешает той же GlobalsDB держать всё в памяти. Это честное тестирование. На одной и тоже машине: одинаковый объём ОЗУ и винта для двух БД.

                    Если мы тестируем базу на 10ТБ на глобалах и in-memory базу на 20GB в ОЗУ — это нечестное тестирование. Разные объёмы.

                    При равных объёмах ОЗУ она выигрывает в скорости за счёт оптимизаций, которые десятками лет выдумывались. Делая синхронизацию когда нужно. Если на машине стоит рейд-контроллер с Write-back и батарейкой, то БД на глобалах может скидывать данные на диск хоть каждую десятую секунды. Write-back кэш будет проглатывать их мгновенно.

                    При последнем подходе иногда может получиться даже, что база 10ТБ на глобалах будет быстрее чем in-memory база на 20GB в ОЗУ.
                      +2
                      Можно изложить вот так. Если мы тестируем in-memory БД и БД на глобалах на одинаковом объёме ОЗУ, то ничего не мешает той же GlobalsDB держать всё в памяти. Это честное тестирование. Одинаковый объём ОЗУ и винта.

                      Это по определению не «честное» тестирование, потому что разные задачи и требования.

                      Если на машине стоит рейд-контроллер с Write-back и батарейкой, то БД на глобалах может скидывать данные на диск хоть каждую десятую секунды. Write-back кэш будет проглатывать их мгновенно.

                      Омг. Разница в скорости между памятью и диском как минимум на порядок (в идеальных условиях, в реальности память быстрее). Теперь представьте, что у меня идет непрерывный поток транзакций, полностью забивающий пропускную способность памяти. Что случится с кэшом?
                        +2
                        Это по определению не «честное» тестирование, потому что разные задачи и требования.


                        А я думаю честное. И с чего вы взяли, что задачи разные? Может быть задача одна и та же, раз сравнивали с in-memory БД — скорость. Если на одинаковой ОЗУ, одинаковом размере памяти и одинаковом размере памяти база на глобалах скоростнее, то значит она скоростнее. В чём тут нечестность?

                        Твоя задача скорость — вот и получаешь скорость.
                          –1
                          А я думаю честное. И с чего вы взяли, что задачи разные? Может быть задача одна и та же, раз сравнивали с in-memory БД — скорость. Если на одинаковой ОЗУ, одинаковом размере памяти и одинаковом размере памяти база на глобалах скоростнее, то значит она скоростнее. В чём тут нечестность?

                          В расходах на жесткий диск, которых у in-memory практически нет.
                            +1
                            Ну так это только замедляет базы на глобалах. Так что HDD — это фора. Я, кстати, могу представить как делали такое тестирование — поставили время синхронизации с диском такое же как и у in-memory и прогнали тесты.

                            Ведь и in-memory БД тоже скидывают на диск когда-то.
                            А если данные в in-memory БД чисто на чтение, то можно в базе на глобалах выставить время синхронизации с диском раз в миллион лет.
                              –1
                              Ведь и in-memory БД тоже скидывают на диск когда-то.

                              Зачем?
                                0
                                Чтобы данные не пропали. Ведь далеко невсегда они используются только для кеширования чтения.

                                redis.io/topics/persistence
                                Loading and Saving In-Memory Databases www.sqlite.org/backup.html
                                  +1
                                  В этот момент они перестают быть in-memory, и становятся гибридными. И у них тоже появляется ограничение по пропускной способности, коррелирующее с диском.
                                    +1
                                    Так и есть. Вы правы. Но всё-равно их называют in-memory. Так принято.
                                      0
                                      Кем принято? А главное, в этом случае сравнение «GlobalsDb выигрывает даже у in-memory» вводит в ожидаемое заблуждение: я весь этот тред думал, что GlobalsDb с полным ACID работает быстрее, чем «чистая» in-memory (которая в лучшем случае ACI, а чаще и того нет).

                                      Так внимание, вопрос: расшифруйте, пожалуйста, вашу фразу «как правило глобалы работают со скоростью in-memory БД при этом обеспечивая хранение на диске, транзакции и другие плюшки»? Что подразумевается под «скоростью» — время отклика, пропускная способность, что-то еще? Какие гарантии обеспечивает «глобал», и какие — «in-memory БД»? На какие исследования вы опираетесь, делая это утверждение?
                                        0
                                        А какие гарантии обеспечивает in-memory БД? Если не сбрасывать дамп на диск — никаких.
                                        Под скоростью принимается частота запросов типа SET и/или GET.

                                        В особенности хорошо глобалы работают с массированной вставкой. С гарантией, что могут потеряться данные только за последние 1-2 секунды без использования транзакций. Цифры озвучивал.

                                        Очевидно, что такой уровень целостности in-memory БД предоставить не может. А если сбрасывать дампы in-memory БД на диск каждые 1-2 секунды, то это убьёт её производительность.

                                        Специально для вас только что я провёл тест на вставку, где каждая вставка была обёрнута в транзакцию, и получил скорость 572 082 вставок/секунду. Вставлял 100М значений на обычные винты в RAID5, write-back включён.

                                        У меня нет развёрнутого дистрибутива GlobalsDB, поэтому тестировал на GT.M.

                                        Это очень приличные цифры — примерно такие же как у Redis на hi-end сервере. Проверьте сами. Код M-программы:

                                        mytest
                                        
                                         for i=0:1:50000000 do
                                         . TSTART
                                         . Set ^a(i)=i
                                         . TCOMMIT
                                        
                                          0
                                          Но о чём говорит такой тест?

                                          Гарантированной фиксации транзакций – нет.
                                          Никаких запросов с клиентского приложения – нет, база варится внутри самой себя.

                                          Чтобы адекватно сравнивать с другими СУБД – нужно делать bulk load транзакциями по 1-2 сек. Сделайте такой тест, тогда будет о чём говорить.

                                          А то у вас тест из серии «положим газету в серную кислоту, а журнал – в дистиллированую воду». В Redis кидаем отдельные SET/GET запросы из клиентского приложения, а GT.M варится внутри себя. Это не серьёзно!
                                            –1
                                            Каждый коммит гарантирует фиксацию транзакций.

                                            Так устроено. Если коммит не проходит, то выкидывается ошибка.
                                            Я просто не писал их обработку.

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

                                              Так устроено. Если коммит не проходит, то выкидывается ошибка.
                                              Я просто не писал их обработку.


                                              Для того, чтобы так было, у вас «на обычные винты в RAID5» должны были бы выдать 572 082 IOPS'ов минимум.

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


                                              Хитро или не хитро, но даже гонять данные между тестовой программкой и СУБД – это не тоже самое что когда СУБД пишет сама в себя.
                                                –1
                                                Для того, чтобы так было, у вас «на обычные винты в RAID5» должны были бы выдать 572 082 IOPS'ов минимум


                                                Достаточно иметь write-back кеш с батарейкой, который и даёт такую производительность.
                                                  0
                                                  Достаточно иметь write-back кеш с батарейкой, который и даёт такую производительность.


                                                  Ок. Но это не отменяет того, что в таких тестах всё, как правило упирается в коммуникацию с приложением, которую вы искусственно выключили.
                                                    –1
                                                    Я просто не стал её создавать. Это же всё хобби. Мне за создание тестов не платят.

                                                    Да, результаты были бы другими. Какими?

                                                    Я не знаю. Сетевые задержки снизили бы скорость, а использованная многоядерность повысила.
                                                      –2
                                                      Нашёл данные своего однопоточного тестирования вставки из PHP через Pipe. Удалось получить 51 398 вставок в секунду. Глобалы при этом предварительно обнулялись.
                                                      Всего вставок делалось 1 000 000.

                                                      Вообще такой результат (51K/сек) для очень и очень многих обычных баз недостижим.

                                                      Предположу, что если организовать 6 потоков, по числу ядер в моём проце, то будет более 300 000 инсертов/сек.

                                                      Так как внутри базы в одном потоке скорость более 500 000 инсертов/секунду.
                                                        0
                                                        Вообще такой результат (51K/сек) для очень и очень многих обычных баз недостижим.

                                                        Из чистого любопытства провел тест на рабочем компьютере (SSD, 8Gb RAM, i5-4460). MS SQL Server (без дополнительных оптимизаций, только преаллокация БД) вставляет миллион записей (одной транзакцией) за ~7,5 с, что дает ориентировочно 133К/сек.

                                                        Понятное дело, что тест нереалистичный (все записи идут скриптом в цикле, скрипт выполняется прямо на сервере — зато без какой-либо компиляции, все записи в одной транзакции), но «о чем-то говорит».
                                                          0
                                                          Там же тест на чтение — 4,3M записей читается 14 секунд, это порядка 300К/сек. Причем мой опыт показывает, что львиная доля этого времени уходит на передачу данных между клиентом и сервером, в реальности первый ответ приходит намного быстрее, и если делать потоковую обработку, то получается еще выиграть.

                                                          Опять-таки, никаких оптимизаций.
                                                            –1
                                                            51K — это я получил когда из другого процесса через pipe давал в интерпретаторном режиме команды.

                                                            Так что сделайте тест по моей методике: цикл идёт в PHP, посылайте команды через pipe по одной.

                                                            Если хотите сравнивать по вашей методике, то сравнивать нужно со значением 750K/ секунду, которое у меня в цикле получается.
                                                              +1
                                                              А там, в общем-то, такая же разница, что и внутри сервера — ориентировочно в пять раз (т.е. порядка 10К/сек). Только это «честная» RDBMS с полной транзакционностью, гарантией фиксации и так далее. Если я, скажем, возьму EventStore, то цифры будут «немножко» другие.
                                                                –2
                                                                Я делал тест в цикле с честной полной транзакционностью — что-то около 300K/сек. Цифры я приводил.
                                                                И я бы так уверенно на вашем месте насчёт честности MS SQL не говорил — они используют буфер на запись, судя по поиску в сети.

                                                                Сделайте тест. Цикл на запись с параллельной записью в сокет/или файл, отрубите жёстко питание, чтобы буфера не успели сброситься и сравните максимальный id после перезагрузки в файле и таблице.
                                                                  +2
                                                                  Я делал тест в цикле с честной полной транзакционностью

                                                                  Мы уже как-то выясняли, что не очень понятно, что у глобалов с изоляцией.

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

                                                                  Спасибо, но нет. Я достаточно хорошо отношусь к своему рабочему компьютеру, чтобы так над ним измываться. Коммит транзакции в MS SQL (а он происходит либо по команде, либо, если открытой транзакции нет, по умолчанию после каждой DML-операции) явно дожидается подтверждения от нижележащей системы о фиксации I/O-операции.
                                                                    0
                                                                    явно дожидается подтверждения от нижележащей системы о фиксации I/O-операции


                                                                    Вы говорите как верующий. А с чего вы взяли, что явно? В интернет полно упоминаний о буферах в MsSQL.

                                                                    И что это за гарантии целостности, если вы боитесь разрушения базы или ОС от выключения компа?
                                                                      +1
                                                                      Вы говорите как верующий. А с чего вы взяли, что явно? В интернет полно упоминаний о буферах в MsSQL.

                                                                      Эти упоминания есть прямо в документации. Вместе с рассказом про то, как работает WAL.

                                                                      И что это за гарантии целостности, если вы боитесь разрушения базы или ОС от выключения компа?

                                                                      Эмм, а в Windows, внезапно, полностью транзакционная файловая система (я сейчас не про MS SQL, а про другие процессы, которые на этой машине запущены)? И это я не говорю про то, какую нагрузку прямой power cycle дает на диск.

                                                                      Меня совершенно не радует мысль о том, что тестовая БД, на которой я гоняю нагрузку, выживет, а системный раздел — умрет.
                                                                        –2
                                                                        Поскольку MS SQL работает в среде Windows, тем самым вы признаёте, что реальных гарантий целостности нет вообще.
                                                                          0
                                                                          Вы правда не понимаете, что гарантия целостности обеспечивается не только системой, но и приложением?

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

                                                                          А теперь сравните это с ситуацией, когда какое-то неумное приложение перезаписывает структурированный файл целиком. В этом случае обрыв записи посередине даст нам нечитаемый файл.
                                                                            –2
                                                                            Лучше ответь на прямой вопрос: насколько серьёзны транзакционные гарантии БД, которая работает только в ОС, которая не гарантирует работу после внепланового отключения питания?

                                                                            Стоит ли говорить про честность? А? А вдруг кто-то поверит и данные потеряет?
                                                                              +1
                                                                              насколько серьёзны транзакционные гарантии БД, которая работает только в ОС, которая не гарантирует работу после внепланового отключения питания?

                                                                              Зависит от реализации СУБД, конечно.

                                                                              А вдруг кто-то поверит и данные потеряет?

                                                                              Каким образом?
                                                    +1
                                                    Как раз речь об этом. В MUMPS ты имеешь возможность работать напрямую с базой и там же развернуть сервер данных, а в других базах нет. Это и есть преимущество.
                              0
                              При желании кеш всегда можно забить. Понятно, что в этом случае производительность упадёт. На объёмах данных, когда забьётся ОЗУ, забьётся кеш сравнивать нужно уже с БД, которые хранят данные на диске.

                              Конечно у СУБД на глобалах задачи несколько другие. Более универсальные.
                              Я не пытаюсь доказать, что диск быстрее памяти )))
                                +2
                                Я не пытаюсь доказать, что диск быстрее памяти

                                Тем не менее, вы утверждаете, что система, хранящая данные на диске, работает со скоростью системы, работающей только с памятью. В моем опыте, это достижимо только отказом от гарантий персистентности. Мне интересно, каких еще способов я не знаю.
                                  –1
                                  Вообще-то я ссылался на статью, где GlobalsDB превзошла Redis, а не была равной.

                                  Чудес не бывает. Хорошая оптимизация кода, алгоритмов кеширования, продуманное и экономичное использование памяти. Преимущества долгого развития.

                                  При гигантском размере базы никакое самое мудрое кеширование не спасёт и упрёмся в IOPS. Кстати говоря, не все БД умеют работать со скоростью большей чем IOPSы SSD.
                                    0
                                    Вообще-то я ссылался на статью, где GlobalsDB превзошла Redis, а не была равной.

                                    Можете еще раз привести ссылку? Как я уже писал, та, которую я вижу — битая.
                                      0
                                      У меня открывается
                                      habrahabr.ru/company/intersystems/blog/194818
                                        +2
                                        Там есть только утверждение «для сравнения Globals по тестам работает быстрее, чем Redis», никакими, собственно, тестами не подтвержденное.
                                          0
                                          Для тех кто будет читать этот тред. В этом комменте можно увидеть результаты Redis vs GlobalsDB
                                            +1
                                            Что интересно, Rob Tweed, когда делал сравнительное тестирование не подкручивал буферизацию в GlobalsDB. Он использовал дефолтные настройки. А по дефолту там сброс буферов не реже, чем раз в секунду.

                                            Globals database, standard default install with no performance tweaking.
                                              0
                                              Вы уже весь его код просмотрели?
                                                0
                                                Нет. Я прочитал его отчёт о тестировании. Код, кстати, на github. Я так понимаю в папке examples. Как я понял, он написал специальный менеджер запросов, который их перенаправляет к GlobalsDB, а с ней работает через пул.
                                        0
                                        — GlobalsDB превзошла Redis,

                                        а Redis об этих тестах знает? А то вполне может что специально сконструриванная база под сравнение, как это уже не раз было в истории.
                                          +1
                                          Не знаю.

                                          Если Redis-база помещается в памяти, то как можно её сконструировать, чтобы ухудшить работу?

                                          В самих тестах написано, что Redis тестировалась отдельно на куче SET операций, а потом отдельно на куче GET. Т.е. наиболее выгодный для Redis вариант.

                                          А GlobalsDB на смешанном потоке, что для базы, которая в итоге данные сбрасывает на диск, явно более проигрышный вариант.

                                          GlobalsDB в секунду делала
                                          read: 92,000 / sec И write: 50,571 /sec

                                          А Redis SET: 88,105 /sec ИЛИ GET: 88,573 /sec

                                          Ссылка
                                +1
                                Буферизация? Т.е., пока буфер не наберется (ну или таймаут не случится), записи на диск не будет?


                                Да.
                                Однако насколько я знаю: буферизация записи вещь настраиваемая. Можно её вообще выставить в ноль. А можно раздуть, если нужно для задачи.

                                Причём она может происходить автоматически как на уровне ОС, так и на уровне БД. Можно на уровне БД вообще отключить.
                                  0
                                  Буферизация? Т.е., пока буфер не наберется (ну или таймаут не случится), записи на диск не будет?

                                  Да.

                                  А как же D из ACID-транзакций? Или транзакции в глобалах, про которые вы выше писали — не durable?
                                    +1
                                    Ну а как вы думаете обеспечивают D — Oracle, MySql и MSSQL? У них у всех есть буфера.
                                    D — обеспечивается тем, что при отключении питания не должны повредиться уже проведённые транзакции.

                                    Когда выдаётся команда COMMIT происходит запись на диск, а всё что до COMMIT может храниться в буфере.
                                      0
                                      Ну а как вы думаете обеспечивают D — Oracle, MySql и MSSQL? У них у всех есть буфера.

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

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

                                      Если вы о commit в терминах ANSI SQL, то представьте себе систему, которая пишет большой поток атомарных событий, каждое из которых является неявной транзакцией (и, как следствие, должно быть зафиксировано на диск). Где здесь место буферу?
                                        0
                                        При таком раскладе:
                                        1) когда каждая транзакция принципиальна важна
                                        2) их очень много

                                        место для буфера остаётся только в плате рейд-контроллера с батарейкой и Write-back cache.

                                        Видимо для таких случаев и оставлена возможность установки буфера в ноль.

                                        Хотя с другой стороны, если мы для надёжности пишем сразу на 2 сервера, то можем спокойно буферизировать запись. Если один из них сломается — запишется на втором.
                                          +1
                                          место для буфера остаётся только в плате рейд-контроллера с батарейкой и Write-back cache.

                                          И в этом случае упрется в скорость IO сразу после окончания буфера контроллера, а они не такие и большие.

                                          Хотя с другой стороны, если мы для надёжности пишем сразу на 2 сервера, то можем спокойно буферизировать запись. Если один из них сломается — запишется на втором.

                                          Вы это серьезно? А то, что распределенная гарантия записи в реальности дороже локальной, вы знаете?
                                            –1
                                            А то, что распределенная гарантия записи в реальности дороже локальной, вы знаете?


                                            В каком отношении дороже? Поскольку мы говорим о скорости, рискну предположить, что вы утверждаете, что нельзя организовать распределённую запись со скоростью того же порядка, что и нераспределённую.

                                            Распределённая будет медленнее, конечно, но не на порядок.

                                            Собственно в другом комменте я написал, что если оборачивать в транзакции, то у глобалов примерно на треть замедляется время вставки.
                                              0
                                              В каком отношении дороже?

                                              В отношении времени, необходимого на подтверждение.
                                +1
                                Авторы вот этой статьи утверждают, что GlobalsDB рвёт по тестам Redis.

                                У вас нет ссылки на статью, только ссылка на этот же блог на хабре. Можно, все-таки, ссылку на сам тест? Гугль дает только битую ссылку на форум самого GlobalsDB.
                                  0
                                  Вообще-то я дал ссылку на статью. А в статье говорилось о тестировании.

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

                                  Однако в недрах wayback machine я нашёл инфу о другом тестировании GlobalsDB vs Redis.

                                  Его проводил Rob Tweed. У него можно узнать детали.
                                  http://web.archive.org/web/20120126105627/http://globalsdb.org/forum/topic/70
                                  OK here's a comparison with Redis on the exact same machine — latest version of Redis (2.2.13), standard install.

                                  Started up the Redis server and then ran src/redis-benchmark -n 100000.

                                  In summary:

                                  SET: 88,105 /sec
                                  GET: 88,573 /sec

                                  Bear in mind that this benchmark runs a bunch of SETs, then a bunch of GETs, rather than a combination together, and is presumably optimised to show off Redis in as good a light as possible.

                                  Compare with the results for Node.js + Globals, where both Sets and Gets were happening together:

                                  read: 92,000 / sec
                                  write: 50,571 /sec

                                  Or, in terms of total database hits: 142,571 /sec

                                  ** Looks like Globals is giving Redis (supposedly the fastest thing on the planet) a serious spanking! **

                                  =======================

                                  Here's the full Redis-benchmark trace for anyone interested:

                                  rob@rob-ProLiant-ML115-G5:~/redis-2.2.13$ src/redis-benchmark -n 100000
                                  ====== PING (inline) ======
                                  100000 requests completed in 1.22 seconds
                                  50 parallel clients
                                  3 bytes payload
                                  keep alive: 1

                                  99.87% <= 1 milliseconds
                                  100.00% <= 1 milliseconds
                                  82169.27 requests per second

                                  ====== PING ======
                                  100000 requests completed in 1.22 seconds
                                  50 parallel clients
                                  3 bytes payload
                                  keep alive: 1

                                  99.81% <= 1 milliseconds
                                  100.00% <= 1 milliseconds
                                  82034.45 requests per second

                                  ====== MSET (10 keys) ======
                                  100000 requests completed in 2.21 seconds
                                  50 parallel clients
                                  3 bytes payload
                                  keep alive: 1

                                  67.53% <= 1 milliseconds
                                  99.93% <= 2 milliseconds
                                  99.99% <= 3 milliseconds
                                  99.99% <= 4 milliseconds
                                  100.00% <= 5 milliseconds
                                  100.00% <= 6 milliseconds
                                  100.00% <= 7 milliseconds
                                  100.00% <= 8 milliseconds
                                  45248.87 requests per second

                                  ====== SET ======
                                  100000 requests completed in 1.13 seconds
                                  50 parallel clients
                                  3 bytes payload
                                  keep alive: 1

                                  99.90% <= 1 milliseconds
                                  100.00% <= 1 milliseconds
                                  88105.73 requests per second

                                  ====== GET ======
                                  100000 requests completed in 1.14 seconds
                                  50 parallel clients
                                  3 bytes payload
                                  keep alive: 1

                                  100.00% <= 0 milliseconds
                                  87412.59 requests per second

                                  ====== INCR ======
                                  100000 requests completed in 1.12 seconds
                                  50 parallel clients
                                  3 bytes payload
                                  keep alive: 1

                                  99.98% <= 1 milliseconds
                                  100.00% <= 1 milliseconds
                                  88967.98 requests per second

                                  ====== LPUSH ======
                                  100000 requests completed in 1.13 seconds
                                  50 parallel clients
                                  3 bytes payload
                                  keep alive: 1

                                  99.98% <= 1 milliseconds
                                  99.99% <= 2 milliseconds
                                  100.00% <= 3 milliseconds
                                  100.00% <= 3 milliseconds
                                  88573.96 requests per second

                                  ====== LPOP ======
                                  100000 requests completed in 1.14 seconds
                                  50 parallel clients
                                  3 bytes payload
                                  keep alive: 1

                                  100.00% <= 0 milliseconds
                                  87796.30 requests per second

                                  ====== SADD ======
                                  100000 requests completed in 1.20 seconds
                                  50 parallel clients
                                  3 bytes payload
                                  keep alive: 1

                                  99.95% <= 10 milliseconds
                                  99.96% <= 11 milliseconds
                                  99.96% <= 12 milliseconds
                                  99.97% <= 13 milliseconds
                                  99.97% <= 14 milliseconds
                                  99.98% <= 15 milliseconds
                                  99.98% <= 16 milliseconds
                                  99.99% <= 17 milliseconds
                                  99.99% <= 18 milliseconds
                                  100.00% <= 18 milliseconds
                                  83612.04 requests per second

                                  ====== SPOP ======
                                  100000 requests completed in 1.17 seconds
                                  50 parallel clients
                                  3 bytes payload
                                  keep alive: 1

                                  99.97% <= 1 milliseconds
                                  99.99% <= 2 milliseconds
                                  100.00% <= 3 milliseconds
                                  100.00% <= 4 milliseconds
                                  100.00% <= 4 milliseconds
                                  85763.29 requests per second

                                  ====== LPUSH (again, in order to bench LRANGE) ======
                                  100000 requests completed in 1.12 seconds
                                  50 parallel clients
                                  3 bytes payload
                                  keep alive: 1

                                  100.00% <= 0 milliseconds
                                  88888.89 requests per second

                                  ====== LRANGE (first 100 elements) ======
                                  100000 requests completed in 2.41 seconds
                                  50 parallel clients
                                  3 bytes payload
                                  keep alive: 1

                                  48.07% <= 1 milliseconds
                                  99.31% <= 2 milliseconds
                                  99.99% <= 3 milliseconds
                                  100.00% <= 3 milliseconds
                                  41476.57 requests per second

                                  ====== LRANGE (first 300 elements) ======
                                  100000 requests completed in 4.80 seconds
                                  50 parallel clients
                                  3 bytes payload
                                  keep alive: 1

                                  1.26% <= 1 milliseconds
                                  66.13% <= 2 milliseconds
                                  95.34% <= 3 milliseconds
                                  99.88% <= 4 milliseconds
                                  99.95% <= 5 milliseconds
                                  99.98% <= 7 milliseconds
                                  99.98% <= 8 milliseconds
                                  99.98% <= 10 milliseconds
                                  99.99% <= 11 milliseconds
                                  99.99% <= 13 milliseconds
                                  99.99% <= 14 milliseconds
                                  99.99% <= 16 milliseconds
                                  99.99% <= 17 milliseconds
                                  99.99% <= 19 milliseconds
                                  99.99% <= 20 milliseconds
                                  99.99% <= 23 milliseconds
                                  99.99% <= 24 milliseconds
                                  99.99% <= 26 milliseconds
                                  100.00% <= 28 milliseconds
                                  100.00% <= 30 milliseconds
                                  100.00% <= 32 milliseconds
                                  100.00% <= 34 milliseconds
                                  100.00% <= 36 milliseconds
                                  100.00% <= 38 milliseconds
                                  20842.02 requests per second

                                  ====== LRANGE (first 450 elements) ======
                                  100000 requests completed in 6.68 seconds
                                  50 parallel clients
                                  3 bytes payload
                                  keep alive: 1

                                  0.64% <= 1 milliseconds
                                  29.63% <= 2 milliseconds
                                  74.15% <= 3 milliseconds
                                  91.33% <= 4 milliseconds
                                  99.79% <= 5 milliseconds
                                  99.89% <= 6 milliseconds
                                  99.92% <= 7 milliseconds
                                  99.96% <= 8 milliseconds
                                  100.00% <= 8 milliseconds
                                  14974.54 requests per second

                                  ====== LRANGE (first 600 elements) ======
                                  100000 requests completed in 8.34 seconds
                                  50 parallel clients
                                  3 bytes payload
                                  keep alive: 1

                                  1.31% <= 1 milliseconds
                                  29.83% <= 2 milliseconds
                                  59.73% <= 3 milliseconds
                                  83.49% <= 4 milliseconds
                                  95.88% <= 5 milliseconds
                                  99.78% <= 6 milliseconds
                                  99.91% <= 7 milliseconds
                                  99.95% <= 8 milliseconds
                                  99.95% <= 9 milliseconds
                                  99.98% <= 10 milliseconds
                                  100.00% <= 10 milliseconds
                                  11996.16 requests per second

                                  rob@rob-ProLiant-ML115-G5:~/redis-2.2.13$
                                    +1
                                    А в статье говорилось о тестировании.

                                    Я цитату выше уже привел, это весьма смехотворно.

                                    А тестирование, на которое вы ссылаетесь сейчас, во-первых, было четыре года назад, с тех пор новая мажорная версия редиса успела выйти, во-вторых, оно методологически странно — сравниваются разные операции, не описаны требования к персистентности и консистентности, и так далее.
                                      0
                                      Это было тестирование на скорость. Какие могут быть требования к Redis в области персистентности и консистентности?
                                      Любой сбой питания и база потеряна.

                                      4 года назад Redis тоже была базой in-memory.

                                      Сам факт, что база с автоматическим сохранением на диск + индексация + сортировка смогла опередить in-memory базу удивителен.

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

                                      Хорошо я привёл данные своего собственного тестирования GT.M — если тестировать чисто вставку получал 750 000 вставок/секунду для миллиона вставок (с использованием буферов по дефолту) и 422 000 вставок/сек для 300М вставок (с постоянным скидыванием инфы на диск, кеш забит).

                                      По этой более новой ссылке мужик получал, тестируя Redis, на Macbook Pro 37 000 вставок/сек и 550 000 вставок/секунду на hi-end сервере.

                                      Скачайте Cache (тестовую версвию) или GlobalsDB или GT.M, напишите программу на COS/M из трёх строчек и вы убедитесь в скорости глобалов сами.

                                      Конечно, это очень разные базы данных и для разных задач.
                                        +1
                                        Это было тестирование на скорость. Какие могут быть требования к Redis в области персистентности и консистентности?

                                        Если их не было, то это тоже надо озвучивать.

                                        Сам факт, что база с автоматическим сохранением на диск + индексация + сортировка смогла опередить in-memory базу удивителен.

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

                                        Скачайте Cache (тестовую версвию) или GlobalsDB или GT.M, напишите программу на COS/M из трёх строчек и вы убедитесь в скорости глобалов сами.

                                        Простой вопрос: как COS/M коммуницирует с СУБД? Он находится в ее процессе или отдельно? Если отдельно, то инструкция вставки передается каждая отдельно, или все сразу?
                                          0
                                          Я тестировал, когда COS/M находился в одном процессе.

                                          Rob Tweed делал обёртку из отдельной программы и обгонял Redis при тестировании по HTTP. У него, естественно, были в разных процессах.
                                            0
                                            Я тестировал, когда COS/M находился в одном процессе.

                                            Эээ, а вас не смущает, что на таких объемах затраты на коммуникацию обходятся дороже общения с памятью?
                                              0
                                              Сложный вопрос. Стандартная утилитка Redis'а тестирует в 50 потоков, используя многоядерность. А я на одном ядре, в одном потоке.

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

                                              Скорости получаются одного порядка. И это делает честь и хвалу программистам Caché и GT.M.
                                                0
                                                Скорости получаются одного порядка. И это делает честь и хвалу программистам Caché и GT.M.

                                                Я не могу считать скорости «внутри процесса» и «по одному объекту между процессами» одним порядком, вы меня простите.
                                                  0
                                                  Ну посмотрите у Роба. У него даже получилось побить Redis. Хотя он вообще прослойку использовал. У него база + менеджер запросов + Node.JS. Три процесса, как минимум.

                                                  Если для вас это принципиальный момент использовать последний Redis — повторите тесты.

                                                  Я вообще ссылку приводил, что на hi-end машине с SSD при тестировании бизнес транзакций через сеть была достигнута скорость 22 миллиона транзакции/сек на Caché. Причём они тестировали очень тяжёлое приложение.
                                                    0
                                                    Ну посмотрите у Роба. У него даже получилось побить Redis.

                                                    Четыре года назад. С неизвестной методикой.
                                                      0
                                                      Вообще-то методику он описал. Я её просто в коммент не скопировал. Нужно перейти по ссылке и читать с самого верхнего поста.
                                                        +1
                                                        Да был я там. Понимаете, «дефолтные конфигурации» четырехлетней давности — это уже давно потерянная информация. И разбираться в его коде, чтобы понять, сколько в реальности запросов он порождает, как они работают, что они делают — я тоже не очень горю желанием.
                                                    0
                                                    Вот ссылка на мой тест, когда доступ был извне процесса.
                                                    В одном потоке при вставках по 20 штук скорость была около 299 000 инсертов/сек.
                                                +1
                                                Rob Tweed делал обёртку из отдельной программы

                                                А с чего вы решили, что данные у него были в разных процессах?

                                                Вот цитата из вашего же перевода статьи того же Роба (выделение мое):

                                                Одним из интересных свойств GlobalsDB является то, можно широко использовать синхронное программирование без вреда для производительности, что упрощает работу с ней и позволяет использовать ОО-синтаксис в полном объёме внутри JavaScript. Это возможно из-за уникальной производительности GlobalsDB: она работает в связке c NodeJS как подлинкованный процесс в оперативной памяти (в отличие от многих других NoSQL-баз, которые работают через различные сетевые сокеты).


                                                Это еще одна иллюстрация к тому, что методологию тестирования надо описывать.
                                                  0
                                                  С того, что запросы подавались по http.
                                                  Приложение делающее запросы + Менеджер http-запросов + несколько тредов [Node.js + GlobalsDB].
                                                  Только то, что в квадратных скобках было в одном процессе.
                                                    0
                                                    Где вы нашли, что запросы подавались по http? Где код приложения, делающего запросы?
                                                      0
                                                      Сама архитектура его приложения показывает, что он принимал запросы по http. Иначе зачем ему менеджер http-запросов, который перенаправляет запросы к пулу тредов, где запущены БД?
                                                        0
                                                        Во-первых, если «архитектура приложения показывает», но самого источника запросов нет — это плохой тест. Но лично мне эта архитектура показывает совсем другое.

                                                        Окей, покажите, где там менеджер http-запросов.
                                                          0
                                                          По ссылке, что я приводил указан:

                                                          https://github.com/robtweed/Q-Oper8
                                                            0
                                                            Омг, Q-Oper8 — это не менеджер http-запросов. Ладно, вот вам код теста, взятый по вашей же ссылке:

                                                            Main Node.js server process:

                                                            var scheduler = require('qoper8');
                                                            var params = {poolSize: 4, trace: true, childProcessPath: '/home/rob/gdbwork/gmpChildProc1.js'};
                                                            
                                                            scheduler.start(params, function(queue) {
                                                              console.log("started!!!");
                                                              var startTime = new Date().getTime();
                                                              var total = 0;
                                                            
                                                              var handler = function(actionObj, response, pid) {
                                                                total++;
                                                                if (total % 100 === 0) {
                                                                  var now = new Date().getTime();
                                                                  console.log(total + ": Total elapsed time: " + (now - startTime)/1000 + ": queue length " + queue.length);
                                                                  console.log("response was " + JSON.stringify(response));
                                                                  console.log("original action was: " + JSON.stringify(actionObj.action));
                                                                  console.log("pid = " + pid);
                                                                }
                                                              };
                                                            
                                                              for (var i = 1; i < 1100; i++) {
                                                                scheduler.addToQueue({action: {domNo: i}}, handler);
                                                              }
                                                            }); 
                                                            


                                                            Child process logic:

                                                            var childProcess = require('qoper8').childProcess;
                                                            var ewdDOM = require('ewdDOM');
                                                            
                                                            var params = {
                                                              path:'/home/rob/globals111/mgr',
                                                              trace: false
                                                            };
                                                            
                                                            ewdDOM.start(params,function(db) {
                                                              var actionMethod = function(action) {
                                                                ewdDOM.counters.set = 0;
                                                                ewdDOM.counters.get = 0;
                                                                ewdDOM.counters.data = 0;
                                                                ewdDOM.counters.retrieve = 0;
                                                                ewdDOM.counters.kill = 0;
                                                                ewdDOM.counters.order = 0;
                                                                var docName = 'test' + action.domNo;
                                                                ewdDOM.removeDocument(docName);
                                                            
                                                                var document = ewdDOM.createDocument(docName);
                                                                var node1 = document.createElement("testElement");
                                                                var documentNode = document.getDocumentNode();
                                                                documentNode.appendChild(node1);
                                                                node1.setAttribute("name","rob");
                                                                node1.setAttribute("town","Reigate");
                                                                var newNode = {
                                                                  tagName: "div",
                                                                  attributes: {id: "myNewNode", abc: 123, def: "this will be removed!"},
                                                                  text: "This is a new div"
                                                                };
                                                                var node2 = node1.addElement(newNode);
                                                                newNode = {
                                                                  tagName: "div",
                                                                  attributes: {id: "secondDiv", abc: 'hkjhjkhjk'},
                                                                };
                                                                var node3 = node1.addElement(newNode);
                                                                var imNode = node1.insertIntermediateElement("intermediateTag");
                                                                var pNode = imNode.insertParentElement("newParentTag");
                                                                node2.modifyElementText("New replacement text for myNewNode");
                                                            
                                                                var response = {docName: docName, counters: ewdDOM.counters};
                                                                return response;
                                                              };
                                                            
                                                              childProcess.handler(actionMethod);
                                                            }); 
                                                            


                                                            Где здесь прием и обработка http-запросов?
                                                              0
                                                              А я вам другой кусок кода покажу:

                                                              qoper8.asyncHandler();
                                                              
                                                                var responseHandler = function(requestObj, results, pid) {
                                                                  //console.log("This is the response handler: ");
                                                                  //console.log("** action: " + JSON.stringify(requestObj.action));
                                                                  //console.log("results = " + JSON.stringify(results));
                                                              
                                                                  var response = requestObj.response;
                                                                  var html = "<html>";
                                                                  html = html + "<head><title>Q-Oper8 action response</title></head>";
                                                                  html = html + "<body>";
                                                                  html = html + "<p>Action was processed !</p><p>Results: " + results + "</p>";
                                                                  html = html + "</body>";
                                                                  html = html + "</html>";
                                                              
                                                                  response.writeHead(200, {"Content-Type": "text/html"});  
                                                                  response.write(html);  
                                                                  response.end();
                                                                  qoper8.makeProcessAvailable(pid);
                                                                };
                                                                0
                                                                Какое отношение этот кусок кода имеет к тесту? В каком месте теста он вызывается?

                                                                Нет, серьезно. Я вам показал реальный код теста, опубликованный автором. Это тот самый код, который создает нагрузку. Если вы ему не верите — значит, вы не верите и результатам теста, и весь разговор бессмысленнен. А если верите — то где в нем http?
                                                                  0
                                                                  Это не тест создающий нагрузку. Это библиотека, которая сохраняет DOM в глобалах.

                                                                  У него есть 2 проекта по ссылке:
                                                                  github.com/robtweed/Q-Oper8 — менеджер запросов
                                                                  github.com/robtweed/ewdDOM — библиотека, которая сохраняет DOM в глобалах.
                                                                    0
                                                                    Так. Еще раз.

                                                                    Открываем приведенную вами ссылку, второй пост сверху (Sept. 20, 2011 04:12:02):

                                                                    Here's the details of the DOM benchmark test. [...] All the DOM creation is defined in the child process logic. The main server process just puts 1000 requests on the Q-Oper8 queue and receives the results.


                                                                    И дальше идет приведенный мной код. В котором, кстати, хорошо видно использование Q-Oper8 для разделения двух процессов и вызов ewdDom в дочернем процессе (собственно, var ewdDOM = require('ewdDOM');).

                                                                    Вы серьезно хотите оспорить утверждение о том, что это не код теста? Я его могу практически построчно откомментировать же.

                                                                      0
                                                                      Ну вот вы и сами признали, что процессы разделены, а не являются одним.

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

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

                                                                      Node.JS на хорошей машине позволяет держать миллион соединений. Так что напихать 1000 запросов в очередь не проблема.

                                                                      Если вы хотите оспорить результаты его теста, то должны сказать что его менеджер запросов не способен принять и поместить в очередь 1000 запросов за 3.5 секунды (длина его теста).

                                                                      Признаю — тестирование у него не является строгим доказательством. Он сравнивал сохранение узлов DOM-документов с SET и GET в Redis. Причём он ещё не учёл, что GlobalsDB выполняла дополнительные команды kill (удаления узлов) и data(проверки существования узла), что снижало производительность. Вообще не понимаю зачем он там kill использовал — наверное, удалял деревья.
                                                                        0
                                                                        Ну вот вы и сами признали, что процессы разделены, а не являются одним.

                                                                        Это «дочерние процессы» в терминах Node, у них другая коммуникация, нежели между изолированными процессами ОС (и тем более — приложениями на разных машинах).

                                                                        Так что напихать 1000 запросов в очередь не проблема.

                                                                        Конечно, не проблема. Только потом каждый из этих запросов порождает порядка пятисот обращений к GlobalsDB, и вот эти обращения по какому транспорту идут?

                                                                        А обращения к Redis каждое проходит по TCP/IP loopback, со всеми накладными расходами.

                                                                        Вот хорошая статья о том, что такое redis-benchmark, и почему ее применение в данном случае вообще ни о чем не говорит.

                                                                        тестирование у него не является строгим доказательством.

                                                                        Воообще никаким доказательством, к сожалению.

                                                                        Вот хорошая статья о некорректности подобных тестирований.
                                                                        0
                                                                        Спасибо за обсуждение. Я думаю мы нашли истину. Я склоняюсь к тому, что по возможности сделаю подобное тестирование, но более строгое. Нужно будет сделать абсолютно такой же интерфейс как и у Redis. И потестировать бенчмарковской утилитой redis.

                                                                        Если у вас есть желание самому провести — я буду рад. Скажите тогда мне, чтобы мы не дублировали друг друга.
                                                                          +1
                                                                          Судя по скорости ответа, статьи вы в лучшем случае проглядели по диагонали.

                                                                          GET/SET Benchmarks are not a great way to compare different database systems.
                                                                            0
                                                                            Ответ был написан до того, как вы привели свои ссылки. Не нужно предполагать в людях худшее.

                                                                            Предложите другую КОНКРЕТНУЮ методику тестирования. Только без общих слов.
                                                                              +1
                                                                              Ответ был написан до того, как вы привели свои ссылки. Не нужно предполагать в людях худшее.

                                                                              Извините, был невнимателен, не посмотрел, на что вы отвечаете.

                                                                              Предложите другую КОНКРЕТНУЮ методику тестирования. Только без общих слов.

                                                                              Да в той же статье написано все — выбираем конкретный сценарий использования, дальше реализуем его под обе БД, смотрим, какую нагрузку выдерживает. В качестве сценария можно выбрать, например, shopping cart — создали, запихали k строчек, вывели всю целиком. Соответственно, потом берем и смотрим, сколько параллельных сценариев система выдержит (и что является ограничивающим фактором). Естественно, обе БД должны быть сконфигурированы под одинаковые гарантии durability (если это возможно). Для пущего счастья надо тестировать два разных типа распределения: когда бд на той же машине, что апп, и когда на разных.
                                                                  0
                                                                  if (uri.indexOf('/test/') !== -1) {
                                                                       var action = {query: urlObj.query};
                                                                       var requestObj = {action: action, request: request, response: response, urlObj: urlObj};
                                                                       qoper8.addToQueue(requestObj, handler);
                                                                     }


                                                                  А тут пришедший http-запрос помещается в очередь.

                                                                  Мне вот непонятна ваша мотивация: зачем вы спорите?
                                                                    0
                                                                    Вопрос не в том http или нет, а в том что передача данных между двумя процессами одной системы и передача данных через сокеты — две большие разницы. Например, на Java я легко с помощью обычных хештаблиц достигал сохранение и получение десятков миллионов элементов в секунду на обычном компе (не сервере), потому что не было накладных расходов на сеть. Даже если при этом переодически делать серилизацию данных в файлы раз в секунду, все равно работало бы куда быстрее посылать данные через сеть в стороннею систему. Естественно, при сравнении работы одной системы через сеть даже в пределах одной машины, второй передающий данные напрямую через память можно получить даже десятикратное превосходство.
                                                  0
                                                  По этой более новой ссылке мужик получал, тестируя Redis, на Macbook Pro 37 000 вставок/сек и 550 000 вставок/секунду на hi-end сервере.


                                                  Из комментария там же:
                                                  I was able to achieve this, without any optimizations, on a 512MB VM I made on my server:
                                                  redis-benchmark -r 1000000 -n 2000000 -t get,set,lpush,lpop -s /tmp/redis.sock -P 16 -q
                                                  SET: 838926.19 requests per second
                                                  GET: 375093.78 requests per second
                                                  LPUSH: 844951.44 requests per second
                                                  LPOP: 973709.81 requests per second

                                    +2
                                    Программы на этом языке можно компилировать — и это даёт огромную скорость обработки данных в БД

                                    Наверно, всё-таки не это. А наверно, те свойства глобалов, которые вы перечислили выше.
                                      +2
                                      Если быть точнее, то можно ответить так:
                                      Скорость глобалов = Простая архитектура * Ассемблер * Отточенность алгоритмов * Компиляция COS/M * Продуманные алгоритмы кеширования

                                      Каждый множитель важен. И, наверное, есть ещё какие-то множители, которые только разработчики знают.

                                      Меня лично изумила скорость вставки при первом знакомстве с глобалами.
                                        +1
                                        На сколько я помню в MUMPS АCID достигается ручным программированием каждой ситуации. Он вроде бы есть, но на самом его нет.

                                        Bы уже сумели победить это проблему?

                                        www.computerworld.com/article/2548231/it-project-management/problems-abound-for-kaiser-e-health-records-management-system.html

                                        В общем, было примерно так: Epic политическими маневрами (читай откат) добилась внедрения ее софта, написанного на MUMPS, который имеет проблемы с масштабируемостью и секьюрити данных. Для борьбы с этим используются Citrix сервера, которым объемы тоже не под силу. Ай, маладцы, здорово продумали архитектуру! CIO поплатился головой.
                                          +1
                                          Есть стандартные команды TSTART, TCOMMIT, TROLLBACK.
                                          Если их использовать, то всё будет ACID.

                                          Из статьи я понял, что Citrix и Epic бодаются на тему из-за кого глючит система.
                                          И Epic говорит, что там где они не использовали CItrix проблем не было.

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

                                          Если даже комп у юзера зависнет — он скажет, что система глючит.
                                            0
                                            А можно про I из ACID по подробнее?
                                            На уровне SQL в Caché, даже полноценного READ COMMITTED, когда я смотрел, не было.
                                            Как обстоит дело с глобалами?
                                              0
                                              Я на домашнем компе запустил транзакцию

                                              TSTART
                                              Set ^ff=5

                                              В другом потоке глобал ^ff был неопределён.

                                              Из чего вывод — Isolated.
                                                0
                                                Из чего вывод — Isolated.


                                                Мягко говоря, основание для вывода недостаточно.
                                                  0
                                                  Ваш пример кода или идея для проверки?
                                                    0
                                                    Пример кода уже не раскопаю. Но в SQL изоляция не работала на 2009 год.
                                                    Идея простая – иметь таблицу с числами. Гонять на ней много параллельных транзакций, которые уменьшают одно число и увеличивают другое так, чтобы сумма не изменялась. А-ля перевод денег со счёта на счёт. В параллельной транзакции постоянно считать сумму и проверять, что она действительно неизменна.
                                                      0
                                                      Я попробовал вашу идею. В первом потоке:

                                                      Set ^f1=5,^f2=5
                                                      TSTART
                                                      Set ^f1=4,^f2=6
                                                      Set ^f1=3,^f2=7
                                                      Set ^f1=2,^f2=8


                                                      Во втором параллельном потоке
                                                      TSTART
                                                      Write ^f1+^f2
                                                      10
                                                      Write ^f1+^f2
                                                      10
                                                      Write ^f1+^f2
                                                      10
                                                      Write ^f1+^f2
                                                      10


                                                      Работает.
                                                        0
                                                        Я не совсем это имел ввиду, а вернее совсем не это…
                                                        Нужно намного много данных и много параллельных транзакций.
                                                        Идея простая, на самом деле.
                                                        Чтобы организовать изоляцию, нужны либо блокировки, либо MVCC.
                                                        На блокировках при большом объёме данных и большом потоке транзакций оно неизбежно начнёт тормозить.
                                                        MVCC в Caché нет. Значит оно либо будет тормозить, либо будет нарушать изоляцию. Хотелось понять, какой из вариантов в каких случаях реализуется.
                                                          +1
                                                          Ну либо я не прав на счёт MVCC. Анализа хочется, а не просто вердикта, что всё круто, но не понятно как и почему.
                                                            0
                                                            Вот что я нашёл:

                                                            Caché SQL, относится не к глобалам, а к таблицам на глобалах.
                                                            ISOLATION LEVEL READ UNCOMMITTED (the default)
                                                            ISOLATION LEVEL READ COMMITTED

                                                            GT.M (а вот про SERIALIZE в TSTART)
                                                            Each block has a transaction number that GT.M sets to the current database transaction number when updating a block.
                                                            — очень похоже на описание MVCC.
                                                              +1
                                                              Вот что я нашёл:

                                                              Caché SQL, относится не к глобалам, а к таблицам на глобалах.
                                                              ISOLATION LEVEL READ UNCOMMITTED (the default)
                                                              ISOLATION LEVEL READ COMMITTED


                                                              Да, но READ COMMITTED работает с нарушениями. Проще сказать, что его нет.

                                                              GT.M (а вот про SERIALIZE в TSTART)
                                                              Each block has a transaction number that GT.M sets to the current database transaction number when updating a block.
                                                              — очень похоже на описание MVCC.


                                                              Не похоже, пока больше похоже на блокировочника.
                                                                0
                                                                Serializability is enforced by LOCK commands or, if the SERIAL keyword is specified, by GT.M.

                                                                Выглядит, что и так и так может работать. SERIAL keyword видимо позволяет обойтись без блокировок.
                                                                  0
                                                                  Выглядит, что и так и так может работать.


                                                                  Пока не увидел на это никакого указания.
                                      +1
                                      Как правило глобалы работают со скоростью in-memory БД при этом обеспечивая хранение на диске, транзакции и другие плюшки.

                                      Можно, пожалуйста, ссылку на конкретные тесты?
                                        +2
                                        Программы на этом языке можно компилировать — и это даёт огромную скорость обработки данных в БД по сравнению с интерпретируемым SQL

                                        Ну вот в PostgreSQL есть plv8, он собственно то же компилируется. На PGDay15 обсуждались различные языки для server-side.
                                          +1
                                          Хорошая возможность. Плюс для PostgreSQL.

                                          Ради точности скажу, что в статье по вашей ссылке идёт речь о JIT-компиляции с кешированием результата, а COS/M однократно компилируется в опкоды или вообще в машинные коды (Caché и GT.M делают это немного по-разному).
                                          0
                                          Правильно ли я понимаю, что github.com/Yomguithereal/baobab частный случай глоала?
                                            0
                                            Похоже на то. Я, правда, не нашёл примера как использовать уже сохранённое дерево.
                                            Все примеры начинаются с инициализации нового дерева.

                                            Инициализация там даже более продвинутая: сразу можно JSON подать в конструктор.

                                            На глобалах нужно функцию вызывать, которая JSON сконвертирует в глобал.
                                          +4
                                          Ещё на глобалы можно посмотреть как на многомерный key->value. Многомерный ключ, скалярное value. На след. неделе, надеюсь, напишу статью об этом.
                                            +2
                                            Лучше смотреть на них как на key->value с составным ключом, это куда ближе к реальности. Потому что фактически все ключи пакуются в один составной ключ для B-tree. Это было описано в документации к старым версиям Кашэ. docs.intersystems.com/cache41/prg/prgglobalstorage.html
                                            В более новых версия Кашэ этот раздел документации выпилили, но внутри, скорее всего, ничего не поменялось. Во всяком случае ограничение на длину так и осталось на все ключи вместе, а не по отдельности.
                                              0
                                              Есть сходство с составным ключом в SQL, но это не то. В SQL составной ключ идёт по жёстко заданному набору колонок, а в глобалах абсолютно по всем наборам всех свойств. И его не надо никак определять в схеме данных.

                                              Запросто на обычном key-value хранилище глобал не получится эмулировать. В глобалах ^a(«b»,«b») != ^a(«bb»). Я проверял. Придётся исхитряться. В индексах числа хранятся как числа, строки как строки.

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

                                              Ну а эмулировать транзакции и вовсе не получится. Поэтому лучше не пытаться сделать глобалы на key-value, а использовать готовые.
                                                +1
                                                Если не вникать, что как устроено, то индекс в глобали именно набор ключей. А за остальное отвечает реализация. Глобалы можно построить на любой УПОРЯДОЧЕННОЙ базе ключ-значение. Нужны только операции доступа к первому, последнему, следующему и предыдущему ключам. На ХЕШ таблицах и неупорядоченных базах ключ-значение глобал не построишь. Вопрос только в эффективности такого решения. Реализация глобалов по крайней мере в CACHE по моему опыту очень эффективна.
                                            +1
                                            Концепция глобалов используется в InterSystems Caché (IMHO самый крутой продукт на глобалах), бесплатная GlobalsDB (сделана на ядре Caché), также есть полностью open-source GT.M и в некоторых других БД.

                                              +4
                                              При поиске в интернете можно обратить внимание на порядок появления терминов:
                                              глобал - 1970, nosql - 2009

                                              +3
                                              А я то думал тут расскажут конкретно о структурах данных которые можно использовать для индексации: ну там R-tree X-tree Quad-tree MVP-tree B*-tree, хэш-таблицах, различных cache oblivious и sync oblivious решениях, ну и как это всё вяжется в контексте упомянутых «глобалов». О тех вещах, которые действительно влияют на производительность.
                                                +2
                                                > Удаление поддеревьев — ещё одна сильная сторона глобалов. Для этого не нужна рекурсия. Это происходит невероятно быстро.
                                                На уровне абстракции это звучит мега-круто, но как такое удаление происходит на практике, в физической реализации? Сдаётся мне, помечается только верхний узел, а реальное удаление откладывается до момента компактирования базы, сборки мусора или какого-то эквивалента. Чудес не бывает, чтобы физически удалить информацию, нужно пройти всё поддерево.
                                                  –1
                                                  Насколько я знаю, там используется определённый сборщик мусора. И этот сборщик очень производительный. На моём домашнем компе 200 000 узлов/сек с учётом работы сборщика мусора.
                                                  +5
                                                  Вот у меня к вам только одно острое пожелание. Пишите про структуры данных — отлично! Но зачем при этом гнобить файловые системы?

                                                  Простейшая операция, которая вам все ваши структуры данных поставит в неприличную позу:

                                                  find . -type f |xargs -I dd if=/dev/random of=I oflag=direct bs=1 count=1 seek=1000000000
                                                  


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

                                                  Моя домашнаяя файловая система на зашифрованном томе справляется с этой задачей для 56 первых попавшихся файлов (бедная репа с github'а) за душераздирающие 23с (привет, иноды!), то есть примерно 0.5с на файл.

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

                                                  Не надо гнать на ФС. Это очень сложные и специализированные базы данных, и у них не то, чтобы всё хорошо, но там очень и очень много думали.
                                                    0
                                                    Спасибо за комментарий. Конечно, такие хитрости типа seek=1000000000 сложно (а скорее всего и невозможно) сделать на глобалах. Фрагментация — отдельный вопрос, который мало где вообще решается в автоматическом режиме.

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

                                                    там очень и очень много думали.


                                                    Над глобалами с 66 года тоже очень много думали.
                                                      +2
                                                      Вы думаете, что вы первый, кто придумал предложить object storage в качестве файловой системы?

                                                      Три простые вещи, в которых ваша файловая система должна быть быстрой, чтобы её вообще восприняли в качестве «файловой системы».

                                                      1) random IO (включая запись). Простейший тест: перенесите mysql/postgres с ext4 на вашу файловую систему и посмотрите «сколько транзакций в секунду» будет выполняться.
                                                      2) Трафик виртуальных машин. Положите на вашу файловую систему виртуалку и посмотрите, сколько там будет выполняться что-то невинное (например, debootstrap).
                                                      3) Положите туда почтовую базу любого более-менее приличного почтового клиента (сервера). На выбор: thunderbird, microsoft exchange, evolution.

                                                      Дополнительные условия для того, чтобы быть приличной FS:

                                                      Поддержка partial blocks, поддержка метаинформации (расширенные атрибуты)

                                                      Понимаете, воспринимать файловую систему как 'object storage' можно, только пользы от неё будет — как от object storage'а.

                                                      Я верю, что «над глобалами думали с 66 года». Но это всего лишь структура данных, и делать из неё серебрянную пулю для решения всех проблем файловых систем — мягко говоря, ошибка.
                                                        –1
                                                        Я верю, что «над глобалами думали с 66 года». Но это всего лишь структура данных, и делать из неё серебрянную пулю для решения всех проблем файловых систем — мягко говоря, ошибка.


                                                        Глобалы – это даже не структура данных, а всего лишь красивая обёртка над B-tree.
                                                          0
                                                          Глобалы – это даже не структура данных, а всего лишь красивая обёртка над B-tree.


                                                          Глобалы — это структура данных и способ хранения данных, где в глубине скорее всего зарыто B-tree.

                                                          Впрочем и в реляционных базах тоже зарыто B-tree и где оно только не зарыто…
                                                            +1
                                                            Глобалы — это структура данных и способ хранения данных, где в глубине скорее всего зарыто B-tree.


                                                            Вы не знаете точно?
                                                              +1
                                                              Я не могу отвечать за все СУБД на глобалах. Я всего с тремя так или иначе имел дело. В GT.M зарыто B-tree.
                                                              В Caché зарыто B*-tree + ещё какая-то технология.
                                                                +1
                                                                Очень ждём во второй части детального описания что и как реализовано. Хочется мяса, а манной каши уже достаточно на эту тему было.
                                                                  0
                                                                  Вообще-то я могу дать ссылки на внутреннее устройство сразу, так как не буду в статьях его рассматривать:
                                                                  Caché 1 — свежее, 2 — несколько устаревшее
                                                                  GT.M

                                                                  Что написано, то написано. Подробнее можно только в исходном коде посмотреть.
                                                                    0
                                                                    Скажу по-другому. Не интересна статья, где говориться как круто обращаться к данным вот с таким синтаксисом, а там внутри ядро, которое развивается с 1966 года, поэтому всё быстро и надёжно. А хочется анализ увидеть: реальные решения, их плюсы и минусы.
                                                                      0
                                                                      Насколько реальные? Конкретные сданные проекты или теоретические примеры решений типовых проблем?
                                                                        0
                                                                        Какие решения заложены в реализации глобалов (хотя бы одну, например Open Source GT.M)? Сравнить это с решениями, заложенными в основу других NoSQL СУБД.
                                                                          –1
                                                                          Я настолько задолбал вопросами главного разработчика GT.M, что он мне сказал:

                                                                          Let me refer you to the ultimate source of information, the source code.

                                                                          А там 50% исходного кода на ассемблере.

                                                                          С InterSystems примерно та же история. Я очень много вопросов задаю.

                                                                          Если разберусь с деталями внутреннего устройства — напишу, конечно.

                                                                          Пока просто хочу рассказать об основах. Внутреннее устройство БД интересно немногим…
                                                                            0
                                                                            Сам факт, что база с автоматическим сохранением на диск + индексация + сортировка смогла опередить in-memory базу удивителен.


                                                                            Когда заявляете такие вещи, которые удивляют вас самих, будьте готовы, что объяснение заинтересует многих. Весь исходный код знать не обязательно, нужно понять алгоритмы и структуры данных.
                                                                        +3
                                                                        Вот +1. Из статьи мало что понятно — откуда плюшки-то. Ощущение, что рекламный агент пытается впарить чудо-технологию.

                                                                        «Внутре у ней… эта… неонка!»

                                                                        Господа, ну во-первых, длительность разработки кода никак не говорит о его качестве. Он мог 100500 раз быть переписан.

                                                                        Во-вторых, не стоит махать ассемблером, как чудо-палкой. Современные процессоры могут кучу различных оптимизаций кода, которые человеку сложно учесть. Поэтому этим занимаются компиляторы, а не люди. Если код не совершенно элементарный, то переписывание его на asm не гарантирует вам автоматически бОльшую скорость, увы. Я не говорю, что компилятор всегда сделает лучше, но и обратное тоже не верно.

                                                                        Остальные аргументы — «отточенные алгоритмы», «вылизанная архитектура» — простите меня за прямоту, это рекламный bullshit.
                                                                        А что persistent db обгоняет in memory — это объективно абсурд, говорит лишь о том, что тестирование проведено не корректно.

                                                                        Но автор заинтриговал! =) Посмотрим, что это за зверь. Хотя никаких иллюзий не питаю, если бы это решение было таким офигительно крутым на протяжении десятков лет — о нем бы на любом заборе было написано. Однако этого не наблюдается.
                                                                          0
                                                                          Спасибо за отзыв! Рад, что получилось написать интересно.

                                                                          В США базы данных на глобалах — это многомиллиардный бизнес.

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

                                                                          На западе практически любая транзакция проходит через иерахическую БД: или на глобалах или на подобии глобалов от IBM

                                                                          В своё время в СССР не стали копировать эту технологию, а когда уже решили, то СССР пошёл вразнос.

                                                                          А что persistent db обгоняет in memory — это объективно абсурд


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

                                                                          Если трафик на запись не является колоссальным, а in-memory БД как правило на чтение используется + на сервере есть рейд с write-back, то при условии чрезвычайной оптимизированности кода персистентной БД она может легко обгонять in-memory БД.

                                                                          Ссылки я приводил. Репозиторий на github для повторения тестирования тоже.
                                                                            0
                                                                            Вас ругают, а с Вас всё как с гуся вода…
                                                                              0
                                                                              Но автор заинтриговал! =) Посмотрим, что это за зверь.


                                                                              Польку отзыв кончается позитивно, то я постарался также его и воспринять.
                                                            0
                                                            Но это всего лишь структура данных


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

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

                                                            Вообще пример с ФС был дан, чтобы лучше объяснить идею глобалов.
                                                              +1
                                                              Файловая система стандартная деревянная структура данных далекая от совершенства. В Linuxe постоянно появляются новые файловые системы. Что говорит, что она далека от совершенства. Только в качестве данных в ФС используется пространство устройства которым она управляет. И за пределы теории структур данных ФС не выходят. И реализовать ФС можно на любой деревянной базе.
                                                          +3
                                                          Дисклеймер: статья и комментарии являются личным мнением автора и не являются официальной позицией корпорации InterSystems.
                                                            +1
                                                            Но в случае глобалов мы получаем на выходе структуризованное проиндексированное хранилище, с которым можно в дальнейшем просто и быстро работать. [...] Данные в глобале всегда проиндексированы. Их обход как на одном уровне, так и вглубь дерева, всегда быстр.

                                                            Кстати, об индексах. В «традиционных», как вы выражаетесь, БД под индексированием обычно понимают добавление дополнительных структур данных обеспечивающих более высокую скорость поиска (и иногда чтения). Судя по статье, вы под этим понимаете другое.

                                                            Предположим, у нас есть представленная вашей же картинкой структура, хранящая данные о пользователях:

                                                            Пользователи

                                                            Вы утверждаете, что:

                                                            Данные сортируются по ключу. В дальнейшем при обходе массива первым элементом станет «Sergey Smith», а вторым «John Sidorov». При получении списка пользователей из глобала база не тратит времени на сортировку.


                                                            А теперь давайте рассмотрим два сценария.

                                                            1. Нам надо вывести список пользователей в порядке возрастания их возраста
                                                            2. Нам надо вывести список пользователей, живущих в Москве


                                                            Какие в глобалах есть технологические решения для оптимизации таких запросов?
                                                              0
                                                              Хорошой вопрос.

                                                              Я уточню: в глобалах любой запрос, в котором используются индексы а-ля индексы массива, является одновременно и индексированным в терминах обычных БД.

                                                              То есть запрос значения глобала ^q(x,y,z) будет одновременно выдавать соотвествующее значение массива, которое будет искаться в базе по ранее автоматически созданному индексу на B-tree (x,y,z).

                                                              Касательно второй части вопроса. Для поиска по городу и возрасту нам придётся создать вторичные индексы. Иначе это будет делаться перебором и долго.

                                                              Я бы написал процедуру для сохранения пользователя, обернул бы её в транзакцию. Примерно так:

                                                              insert_user(id, name, age, city)
                                                               TSTART
                                                               Set ^a(id)=name
                                                               Set ^a(id, "city")=city, ^a(id, "age")=age
                                                               Set ^a("i_city",city, id)=1
                                                               Set ^a("i_age",age, id)=1
                                                               TCOMMIT


                                                              Когда нам потребовалось бы вывести список пользователей по возрасту, то с помощью функции $Order(^a(«i_age»)) я бы получил список id пользователей отсортированных по возрасту.

                                                              Далее по id вывел бы его с подгрузкой остальных полей.

                                                              Для вывода москвичей я бы вызвал функцию $Order(^a(«i_city»,«Moscow»)). Эта функция нашла бы моментально первого москвича, а далее я бы вызывал в цикле $Order(^a(«i_city»,«Moscow», предыдущий_id)), а потом также по id выводил бы сопутствующие поля.

                                                              Глобалы похожи на Си — это низкоуровневая вещь, однако работает быстро. Над ними в Caché есть объектная и SQL-надстройки, которые позволяют упростить работу.
                                                                0
                                                                Я уточню: в глобалах любой запрос, в котором используются индексы а-ля индексы массива, является одновременно и индексированным в терминах обычных БД.

                                                                Я боюсь, что у нас с вами фундаментально разное понимание «индексации». В моем понимании, проход по «индексам» глобала эквивалентен индексам традиционных БД только тогда, когда эти «индексы» являются значениями. Иными словами, ^a("+7926X") — это индекс (потому что он имеет семантику «найди такой элемент, идентификатор которого равен „+7926X“), но ^a("city") — не индекс, потому что его семантика — »найди таблицу City".

                                                                Давайте попробуем зайти с другой стороны. Какова алгоритмическая сложность вставки элемента в глобал (пока рассмотрим только один уровень) и его получения оттуда?

                                                                Для поиска по городу и возрасту нам придётся создать вторичные индексы.

                                                                И сами глобалы этого никак не умеют?

                                                                Я бы написал процедуру для сохранения пользователя, обернул бы её в транзакцию.

                                                                Тем самым мы (а) увеличили количество операций записи приблизительно вдвое, увеличили время блокировки системы настолько же и, что интереснее, (б) теперь должны поддерживать связность этих «вторичных индексов» все последующие разы.

                                                                С моей точки зрения это означает, что глобалы — не индексированы; потому что характеристика «доступ по ключу в дереве» характерна дереву, а не индексам, а собственно индексов в понимании «традиционных БД», т.е. возможности автоматически оптимизировать доступ по не-ключам, мы пока не нашли.
                                                                  +1
                                                                  Насчёт ^a(«city»). Представьте, что подобных свойств миллион. В глобале не будет перебора, будет использование дерева. Очень быстро узел ^a(«city») будет найден. Внутри глобалов, как правило, та или иная разновидность B-tree.

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

                                                                  И сами глобалы этого никак не умеют?


                                                                  Нужно различать глобалы и то, что можно на них сделать. В том же Caché можно работать с глобалами через SQL (или объектный доступ) — в этом случае все эти низкоуровневые операции создания вторичных индексов, поиска по ним, поддержания целостности будут делаться невидимо для программиста.

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

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

                                                                  Глобалы — это не замена SQL. Это возможность делать скоростные NoSQL приложения, специализированные БД и многое другое. Это низкоуровневая вещь. Поэтому основные операции с ними можно выучить за час, чего не скажешь об операторах SQL.
                                                                    +1
                                                                    Насчёт ^a(«city»). Представьте, что подобных свойств миллион. В глобале не будет перебора, будет использование дерева.

                                                                    А вы считаете, что обращение к нужно таблице в «традиционных» БД делается перебором? Я сейчас говорю о семантике, а не о скорости, в любом случае.

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

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

                                                                    Именно поэтому я призываю вас не писать, что «глобалы дают проиндексированное хранилище». Глобалы дают дерево, с характерными для него особенностями (включая время доступа). Индексы — это другая сущность, ее глобалы не дают.

                                                                    Кстати, вы так и не ответили про алгоритмическую сложность операций на глобалах.
                                                                      0
                                                                      ^a(«city») — это не таблица.
                                                                      Чтобы вам было проще используйте аналогию, что глобал — это таблица — ^a.

                                                                      В глобалах семантика своя. Я пытаюсь найти аналогии для людей из известных терминов.

                                                                      И, как следствие, результаты получаются различными.


                                                                      Какие именно результаты различны?

                                                                      Когда я запрашиваю ветви узла (команда $Order) ^a(5, «city») это эквивалентно тому что я делаю:

                                                                      Select city from a where id=5 Order by city

                                                                      (Допустим в таблице содержатся маршруты коммивояжера)

                                                                      Если мне нужно только значение ^a(5, «city») это эквивалентно
                                                                      Select city from a where id=5

                                                                      (Таблица с личными данными)

                                                                      Алгоритмическая сложность — точно не знаю. Учитывая, что всё построено на разновидностях B-tree примерно O(log(N)).

                                                                      На практике при вставке 300M значений я не замечал никакого торможения, кроме как из-за забивающегося кеша на запись. Когда европейское космическое агентство тестировало вставку на протяжении нескольких дней, то тоже не заметили торможения вызванного разбуханием глобала.
                                                                        +1
                                                                        В глобалах семантика своя. Я пытаюсь найти аналогии для людей из известных терминов.

                                                                        Вот я вам и показываю ошибку в вашей аналогии.

                                                                        Какие именно результаты различны?

                                                                        Семантика индекса и основного ключа — разные вещи. И семантика доступа к данным и метаданным — разные вещи.

                                                                        Вот возьмем, казалось бы, банальную вещь: словарь (построенный на хэш-таблице), значениями которого являются объекты (скажем, пользователи), а ключом — идентификаторы объектов (скажем, UID пользователя). Доступ к конкретному объекту стоит нам O(1) (это свойство хэш-таблицы). Доступ к конкретному свойству конкретного объекта — тоже O(1), потому что доступ к свойству объекта является элементарной операцией языка. Итого мы имеем структуру, болезненно похожую на ваш двухуровневый глобал, и со стоимостью операций O(1). Является ли она «индексированной»? Нет. Точнее, является — только по «первичному ключу», что в БД эквивалентно кластерному индексу по первичному ключу. Вы не можете за те же O(1) получить пользователя с заданным емейлом. Аналогично и глобалы (в вашем описании) не являются индексированной структурой. Они являются структурой, которая дает быстрый доступ по (строго одному для каждого значения) ключу, но никак не индексирует сами значения.

                                                                        Поэтому еще раз вас прошу — не вводите в заблуждение, не пишите, что глобалы, помимо основных своих качеств, еще и дают индексированное хранилище. Оно не индексированное. Оно просто дерево, с характерными для дерева O(log n).
                                                                          0
                                                                          Хорошо. Вы фокусируете меня на том, что индекс — это более общее понятие, чем первичный ключ.

                                                                          Я согласен.

                                                                          Индекс работает O(log(N)) и дерево работает O(log(N)). Потому что индексы внутри тоже деревья. Отличие семантическое очень тонкое.

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

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

                                                                          А ведь я могу заполнить все нужные деревья, а потом обращаться к ним вообще из SQL. И интерпретатор SQL в Caché автоматически будет использовать мои вспомогательные деревья и использовать их как вторичные индексы.

                                                                          Вопрос: чем это отличается от индексированного хранилища?

                                                                          Я вижу лишь отличие на уровне глобалов — там мне действительно придётся писать доп. процедуры для выборок с использованием вторичных индексов и там нет той высокоуровневой работы с индексами, свойственной SQL.
                                                                            +2
                                                                            Индекс работает O(log(N)) и дерево работает O(log(N)). Потому что индексы внутри тоже деревья. Отличие семантическое очень тонкое.

                                                                            Да ничего тонкого. Дерево — это структура данных. Индекс — это инструмент в БД. Второе может быть построено на первом (а может и не быть).

                                                                            Вопрос: чем это отличается от индексированного хранилища?

                                                                            Тем, что вы поддерживаете целостность самостоятельно (и потери на этом в ваших тестах не учтены). Поэтому писать, что глобалы дают индексированное хранилище — неверно. Вы его делаете поверх глобалов, как дополнительную структуру.

                                                                            Я вижу лишь отличие на уровне глобалов

                                                                            А мы глобалы и обсуждаем в этой статье.
                                                                            0
                                                                            Я согласен с тем, что без использования вспомогательных деревьев не получится сделать быстрый поиск по другим полям.
                                                                  +1
                                                                  В MUMPS используется несколько другая терминология, чем в традиционных БД. Индексом называется то, что обычно называется первичным ключем. Если использовать традиционную терминологию то индексов в глобалях нет, и более того в общем случае их быть не может. Глобаль и реляционная база это не изоморфные структуры. Любую реляционную таблицу можно представить в виде дерева, но не наоборот. Реляционная таблица это частный случай дерева с одинаковым количеством индексов и данными только на последнем уровне. А если есть данные на промежуточных уровнях, то для такого дерева никакие индексы не построишь. И требование наличия индексов для глобали некорректны.
                                                                    0
                                                                    Любую реляционную таблицу можно представить в виде дерева, но не наоборот.

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

                                                                      Что мы с некоторым успехом и доказали inetstar.

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

                                                                      Любую реляционную таблицу можно представить в виде дерева, но не наоборот.

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

                                                                        Что-то я явно не проснулся еще. «когда даже терминология различается, сложно провести аккуратные аналогии».
                                                                          –1
                                                                          Мне кажется вы преувеличиваете роль индексов в базах данных. Насколько я знаю NoSql базы не имеют индексов и прекрасно существуют. Это изобретение SQL баз и достаточно сомнительное. Индексация базы как любое дублирование порождает кучу плохо разрешимых проблем. Если индексировать первичную информацию, то никогда нельзя быть уверенным в достоверности индекса. Критические к достоверности данные индексировать фактически нельзя. Не критические требуют проверки индекса и его обслуживания. Использовать индексы на временных данных вообще бессмыслица. С таким же успехом можно построить необходимые глобали, что тоже мало полезно. Поэтому я не считаю индексацию базы великим преимуществом.
                                                                            +4
                                                                            Насколько я знаю NoSql базы не имеют индексов и прекрасно существуют.

                                                                            Вы знаете неправильно. Индексы в MongoDB, индексы в RavenDB.

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

                                                                            Ну так транзакции же.

                                                                            Это изобретение SQL баз и достаточно сомнительное

                                                                            Прекрасно. Предложите другой способ выбирать всех клиентов, живущих в Пенсильвании, без прямого перебора их записей.
                                                                              0
                                                                              Другого способа нет. В подобном случае пользуюсь прямым перебором.
                                                                                0
                                                                                Я так понимаю, что производительность вас в этом случае не волнует.
                                                                                  +4
                                                                                  Мне кажется, что misha_shar53 ошибается. Как и любая другая NoSQL система, глобалы подразумевают, что данные организовываются с учётом последующих выборок. Т.е. естественно, что если есть необходимость «выбирать всех клиентов, живущих в Пенсильвании», будет как вариант, напрограммирован соответствующий индексный глобал, или в городах будет ссылка на его жителей, или будет индекс в глобале, или ещё как-то. Но и «полный перебор», в некоторых случаях может быть не так уж и затратен.
                                                                                  Тут как и с индексами в SQL системах, не все они одинаково полезны.

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

                                                                                  А что бы не допустить такого, есть и упомянутые вами транзакции и полезная вещь — блокировки lock.
                                                                                    +1
                                                                                    Мне тоже кажется, что он ошибается, но поскольку я не работаю с глобалами, я не могу это ни доказать, ни опровергнуть.
                                                                                      0
                                                                                      ## Если использовать традиционную терминологию то индексов в глобалях нет, и более того в общем случае их быть не может.

                                                                                      в мампсе и индекс есть и перебирают все — последовательно, каждая строка имеет индекс (глобал) у нее связка со следующей строкой этого глобала и того же уровня… нужна пенсильвания пиши:
                                                                                      ^v[«USA»,«Пенсильвании»,«user_1»] = _
                                                                                      ^v[«USA»,«Пенсильвании»,«user_2»] = _
                                                                                      ^v[«USA»,«Пенсильвании»,«user_3»] = _
                                                                                      ^v[«USA»,«Пенсильвании»,«user_4»] = _

                                                                                      My_Global = ^v[«USA»,«Пенсильвании»]

                                                                                      и фор цикл по глобалу My_Global
                                                                                        +2
                                                                                        Спасибо, а как из этой структуры мне узнать, где живет user_1?
                                                                                    0
                                                                                    Безусловно волнует. Но из 2-х зол я всегда выбираю меньшее. А в моих случаях всегда корректность данных была важнее скорости. Но возможны варианты.
                                                                                      +1
                                                                                      А в чем ваши проблемы с корректностью данных? В глобалах нет транзакционной целостности?
                                                                                        0
                                                                                        В глобалах есть транзакции. Теоретически все должно оставаться корректным. На практике это не так. Редко но бывает нарушение целостности данных, при этом это никак не проявляется внешне кроме как проверкой специальными утилитами или противоречий в выходных документах. А так как приложение работает у заказчика и нами постоянно не администрируется, обнаружиться нарушение целостности данных может поздно. Это проблема не только индексов, а любого дублирования первичных данных.
                                                                                          0
                                                                                          В глобалах есть транзакции. Теоретически все должно оставаться корректным. На практике это не так.

                                                                                          Я правильно вас понял: в глобалах есть транзакции, но они не гарантируют целостность данных?
                                                                                            0
                                                                                            Именно так. Транзакции есть, но они не всегда гарантируют корректность результатов. С подобной проблемой я сталкивался и в SQL базах при использовании индексов. Мир к сожалению не идеален и очень сложен. Сказать точно в какие моменты это происходит и почему я не могу, но с подобными явлениями я переодически сталкиваюсь.
                                                                                              +2
                                                                                              Транзакции есть, но они не всегда гарантируют корректность результатов.

                                                                                              Просто… вау. Нет, серьезно. А inetstar так долго доказывал нам, что в глобалах есть «честные» ACID-транзакции…

                                                                                              С подобной проблемой я сталкивался и в SQL базах при использовании индексов.

                                                                                              Вы, конечно же, можете привести конкретный пример с указанием СУБД?
                                                                                                0
                                                                                                Конечно InterBase. А вы что верите сказкам, что такого не бывает в природе?
                                                                                                  +1
                                                                                                  А вы что верите сказкам, что такого не бывает в природе?

                                                                                                  Я верю своему опыту, который говорит, что при отсутствии аппаратных проблем СУБД, с которыми я работал (MS SQL в основном, немного Oracle и DB/2), никогда такого не допускали.

                                                                                                  Каждый — каждый — раз, когда мы расследовали ошибку «у нас тут кривая транзакция», выяснялось, что ошибка в прикладном коде.
                                                                                                    0
                                                                                                    Ну значит у нас разный опыт. А насчет отсутствия аппаратных проблем ничего сказать не могу в обоих случаях они могли иметь место. Что происходит у заказчика мы постоянно не контролировали. Возможно после выключения питания при повторном пуске шло какое то восстановление из журналов. Может еще что. Обычная дворцовая жизнь. Но системы в обоих случаях продолжали работать и ничего криминального не сообщали.
                                                                                                      0
                                                                                                      Но системы в обоих случаях продолжали работать и ничего криминального не сообщали.

                                                                                                      Значит, система не предоставляет гарантий по консистентности/персистентности. Не надо с такими работать безотносительно того, есть там индексы, или нет.

                                                                                                      (конечно, если вам важна консистентность данных, а не то, что вы систему в качестве кэша используете)
                                                                                                        0
                                                                                                        Конечно не предоставляет. Но других систем у рядовых заказчиков нет. Обычные китайские компы. А вы наверно обитаете в стране чудес? Мне бы к вам. Тогда и буду использовать индексы и зависимые глобали. А пока приходится как то выкручиваться. Так и живем.
                                                                                                          +1
                                                                                                          Но других систем у рядовых заказчиков нет.

                                                                                                          Ну не знаю, мы как-то находили.

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

                                                                                                          Если вы работаете, как вы выражаетесь, с «критическими» данными, для них такие системы использовать просто нельзя.
                                                                                                            0
                                                                                                            Согласен с вами, нарушения целостности будут. Но в этом случае на порядок реже, да и они будут заметнее. Легче это проконтролировать. Да и все равно другого выхода нет.
                                                                                                              0
                                                                                                              Да почему нет-то? Используйте СУБД с гарантией персистентности, и все у вас будет хорошо на сколько-нибудь приличном оборудовании.
                                                                                                                0
                                                                                                                Хороший совет. Жаль не могу им воспользоваться.
                                                                                                          0