PHP-модуль для работы с иерархическими данными в БД InterSystems IRIS
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 операций вставки в секунду на обычном железе), по двум причинам:
- Глобалы — это более низкоуровневая абстракция, чем SQL,
- Базы на глобалах уже в продакшене десятки лет и за это время код их успели вылизать и досконально оптимизировать.
Подробнее о глобалах в цикле статей «Глобалы — мечи-кладенцы для хранения данных»:
Деревья. Часть 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
Инструкция по установке модуля на английском и русском языках.
Функции модуля:
Если вы хотите самостоятельно поиграться с модулем, то:
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
- Иметь Linux. Я тестировал под Ubuntu, под Windows модуль тоже должен работать, но я не тестировал.
- Скачать бесплатную версию:
- Установить модуль cach.so в PHP по инструкции.
В docker-контейнере на своём персональном компьютере (AMD FX-9370@4700Mhz 32GB, LVM, SATA SSD) для интереса я сделал два примитивных теста на скорость вставки новых значений в БД.
- Вставка 1 миллиона новых элементов в глобал заняла 1.81 секунды или 552К инсертов в секунду.
- Обновление значения в одном и том же глобале 1000000 раз заняла 1.98 секунды или 505K обновлений в секунду. Интересный факт, что вставка происходит, чем обновление. Видимо это следствие изначальной оптимизации базы данных под быструю вставку.
Понятно, что эти тесты не могут претендовать на точность и полезность, так как примитивны, сделаны в контейнере. На более мощном железе с дисковой системой на PCIe SSD можно добиться десятков миллионов вставок в секунду.
Что можно доделать и текущее состояние.
- Можно добавить полезные функции для работы с транзакциями (их и сейчас можно использовать через iris_exec).
- Не реализована функция возврата всей структуры глобала, чтобы из PHP не заниматься обходом глобала.
- Не реализована функция сохранения PHP-массива как поддерева.
- Не реализован доступ к локальным переменным базы. Только через iris_exec, хотя лучше через iris_set.
- Не реализован обход глобала в глубь в обратном направлении.
- Не реализован доступ к БД через объект с использованием методов (аналогичным текущим функциям).
Для продакшена текущий модуль, пожалуй, ещё не готов: не было тестов под высокие нагрузки и на утечки памяти. Однако если это кому-то потребуется, то обращайтесь ко мне (Сергей Каменев updates@mail.ru).
Заключение.
Долгое время миры PHP и иерархических баз на глобалах практически не пересекались, хотя глобалы предоставляют сильный и быстрый функционал на специфических типах данных (медицинские, персональные).
Надеюсь, что этот модуль послужит толчком для экспериментов программистов PHP с глобалами и программистам ObjectScript для простой разработки веб-интерфейсов на PHP.
P.S. Спасибо за внимание!