PHP-модуль для работы с иерархическими данными в БД InterSystems IRIS

image PHP с начала своих времён славен (и критикуем) тем, что поддерживает интеграцию с массой библиотек, а также с практически со всеми БД существующими на рынке. Однако в силу каких-то странных причин в нём не было поддержки иерархических баз данных на глобалах.

Глобалы — это структуры для хранения иерархической информации. Они чем-то напоминают базы «key → value» только с тем отличием, что ключ может быть многоуровневым:

Set ^inn("1234567890", "city") = "Moscow"
Set ^inn("1234567890", "city", "street") = "Req Square"
Set ^inn("1234567890", "city", "street", "house") = 1
Set ^inn("1234567890", "year") = 1970
Set ^inn("1234567890", "name", "fisrt") = "Vladimir"
Set ^inn("1234567890", "name", "last") = "Ivanov"

В этом примере на встроенном языке ObjectScript в глобал ^inn, который хранится на жёстком диске (об этом говорит значок ^ перед названием глобала), сохраняется разноуровневая информация.

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

Глобалы поддерживают множество функций для работы с иерархичностью: обход ключей на каждом уровне отдельно, удаление, копирование и вставка целых деревьев и отдельных узлов. Ну, а также как и любая хорошая БД ACID транзакции. Всё это происходит чрезвычайно быстро (порядка 105–106 операций вставки в секунду на обычном железе), по двум причинам:

  1. Глобалы — это более низкоуровневая абстракция, чем SQL,
  2. Базы на глобалах уже в продакшене десятки лет и за это время код их успели вылизать и досконально оптимизировать.

Подробнее о глобалах в цикле статей «Глобалы — мечи-кладенцы для хранения данных»:

Деревья. Часть 1.
Деревья. Часть 2.
Разреженные массивы. Часть 3.

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

Я люблю PHP (и разрабатываю на нём) и хотел поиграться с глобалами. Готового модуля не было. Я написал в InterSystems c просьбой создать его. Ожидание ни к чему не привело и в итоге мы (я вместе со своим студентом-дипломником) сделали модуль сами. InterSystems проспонсировала разработку в рамках образовательного гранта.

Вообще говоря, InterSystems IRIS — это мультимодельная СУБД, поэтому из PHP с ней можно работать через ODBC используя SQL, но меня интересовали именно глобалы, а такого коннектора не было.

Итак, модуль доступен для PHP 7.x (тестировался под 7.0–7.2). На текущий момент он может работать только с InterSystems IRIS и Caché, установленной на том же хосте.

Страница модуля на OpenExchange (каталог проектов и дополнений для разработчиков на InterSystems IRIS и Caché).
Там есть полезный раздел DISCUSS, в котором люди делятся опытом использования.

Скачать тут:

https://github.com/intersystems-ru/php_ext_cache


Скачать репозиторий из командной строки:
git clone https://github.com/intersystems-ru/php_ext_cache

Инструкция по установке модуля на английском и русском языках.

Функции модуля:

Если вы хотите самостоятельно поиграться с модулем, то:

Специально для пользователей Хабра был создан Dockerfile для сборки образа.
git clone https://github.com/intersystems-community/php_ext_iris
cd php_ext_iris/iris
docker-compose build
docker-compose up -d

Тестируем демо-страницу на localhost:52080 в браузере.
PHP файлы, которые можно править и играться с ними лежат в папке php/demo и будут подмонтированы внутрь контейнера.

Чтобы потестировать IRIS используйте логин admin, пароль SYS.

Чтобы войти в настройки IRIS используйте следующий УРЛ:
localhost:52773/csp/sys/UtilHome.csp

Чтобы войти в консоль IRIS этого контейнера используем команду:

docker exec -it iris_iris_1 iris session IRIS

Для самостоятельной установки модуля под InterSystems Caché
  1. Иметь Linux. Я тестировал под Ubuntu, под Windows модуль тоже должен работать, но я не тестировал.
  2. Скачать бесплатную версию:
  3. Установить модуль cach.so в PHP по инструкции.

В docker-контейнере на своём персональном компьютере (AMD FX-9370@4700Mhz 32GB, LVM, SATA SSD) для интереса я сделал два примитивных теста на скорость вставки новых значений в БД.

  • Вставка 1 миллиона новых элементов в глобал заняла 1.81 секунды или 552К инсертов в секунду.
  • Обновление значения в одном и том же глобале 1000000 раз заняла 1.98 секунды или 505K обновлений в секунду. Интересный факт, что вставка происходит, чем обновление. Видимо это следствие изначальной оптимизации базы данных под быструю вставку.

Понятно, что эти тесты не могут претендовать на точность и полезность, так как примитивны, сделаны в контейнере. На более мощном железе с дисковой системой на PCIe SSD можно добиться десятков миллионов вставок в секунду.

Что можно доделать и текущее состояние.

  1. Можно добавить полезные функции для работы с транзакциями (их и сейчас можно использовать через iris_exec).
  2. Не реализована функция возврата всей структуры глобала, чтобы из PHP не заниматься обходом глобала.
  3. Не реализована функция сохранения PHP-массива как поддерева.
  4. Не реализован доступ к локальным переменным базы. Только через iris_exec, хотя лучше через iris_set.
  5. Не реализован обход глобала в глубь в обратном направлении.
  6. Не реализован доступ к БД через объект с использованием методов (аналогичным текущим функциям).

Для продакшена текущий модуль, пожалуй, ещё не готов: не было тестов под высокие нагрузки и на утечки памяти. Однако если это кому-то потребуется, то обращайтесь ко мне (Сергей Каменев updates@mail.ru).

Заключение.

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

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

P.S. Спасибо за внимание!

© Habrahabr.ru