Tag hierarchy

OrientDB или здравствуй дерево !

Несмотря на избитость и некую внешнюю пошарпанность темы, я все-таки подниму ее из пыли земной, слегка отряхнув, зажгу и воткну пылающим факелом знаний в темную ж… БД-невежества.

«Если в руке SQL база, то все вокруг кажется таблицами»

Народная программистская пословица

2D Тупик

И года не может пройти в нашем беспокойном мире без революций в той или иной сфере, конечно, не считая вечно кровавого политического болота. К счастью не политики, а именно окружающий мир (где они, к сожалению, тоже присутствуют) и определяет как будут выглядеть базы сегодняшнего и завтрашнего для. Наблюдается опеределенная тенденция — устаревшие реляционные 2D базы со своим псевдо-стандартным SQL отходят в прошлое, их место занимают более востребованные облачные БД с новыми возможностями и новыми языками запросов (NewSQL), а порой вовсе без них (NoSQL). Двухмерные таблицы старых БД не в состоянии обеспечить поддержку, по крайней мере, двух самых насущных парадигм, применяемых при решении современных социально-ориентированных задач, в которых первостепенную роль играют связи (виды связей) и/или отношения между обьектами. Первая парадигма — это иерархичность (трехмерность) классов обьектов и связей между ними (for ex. тот же социум со своими связями разного масштаба, производственные процессы, базы знаний и т.д.) Вторая — обьектно-ориентированная природа данных в задачах и, как следствие, желание естественным образом сохранить ее в БД. Увы, 2D базы никогда не были в состоянии хранить и более менее сносно осуществлять поиск в древовидных и графоподобных структурах данных, так как тяжеловесность доступных алгоритмов просто выходит за всякие рамки. Приведу яркий пример для MySQL (привет фэйлбук !) — табличка времени исполнения, на современном 4-ядерном лаптопе с 8GB RAM, запроса выдачи дерева друзей для всего 1000 юзеров при разной, и совершенно не впечатляющей глубине, (до 5) дерева [взято из книги "Neo4j in Action"] :

ГлубинаВремя выполнения (секунд) для 1`000 юзверейРезультат (записей)
20.028~900
30.213~999
410.273~999
592`613.150~999

Вот оказывается почему большинство социальных сетей жутко тормозит и глючит ;)

А если мы усугубим ситуацию и добавим поиск сразу по нескольким разным классам отношений между обьектами, например — выдать дерево всех друзей юзеров и их друзей и друзей их друзей и т.д., которые смотрели фильм X И играют в игру Y, поиск производить по уровню вложенности не более 5, результат ограничить уровнем вложенности 3 ? Думаю любая 2D база тупо загнется при таком JOIN запросе даже на смешном, по понятиям социальных сетей (несколько тыщ), количестве пользователей. А если типов связей больше сотни и дерево глубиной в сотни тысяч уровней ? Об осуществлении каких либо сложных аналитических операций над таким обьемом данных здесь можно сразу напрочь забыть, тут бы как-то справится со стандартной web нагрузкой. Та база, о которой пойдет речь в этой заметке легко осуществляет такой поиск за единицы миллисекунд — можете сопоставить ее мощность с вышеприведенными данными для 2D баз. Эх, как же сложно выживать фэйлбукам в обнимку с MySQL, т.е. с орками^H^H^H^H^HOracle в этом жестоком мире …

От синематографа к HD3D

Занимаясь, на досуге, разработкой облачной персональной базы знаний, я вновь уперся в старое и уже порядком доставшее противоречие — невозможность эффективного использования современных (в том числе и облачных) БД для простеньких задач представления знаний. Эти БД — просто «не тянут», применяемый там язык SQL похоронен под бетонным непрактичным стандартом (несмотря на трезвый анализ могучего Стонебрейкера «Future Trends in Database Systems», сделанный еще в конце 80-ых, который видимо пофигу, считающими себя не менее могучими, разработчикам современных БД), а их «универсальные» типы данных — это все тот же каличный набор от древних компьютерных мастодонтов 60-х, с небольшими вариациями.

Гуголь на вопрос «что же теперь делать ?» выдал две фотки грустных обезьянок, одну карикатуру на Гитлера, рисующего черной краской на стене надпись «П.здец всему» и ссылку на OrientDB. О, пожалуй, последнее стОит более тщательного рассмотрения …

«Выборы, выборы, кандидаты …» (C) Ленинград

Так какие же сытные ништяки обещает нам OrientDB по сравнению с общепринятыми решениями ?

Кратко :

  1. обьектная база по определению, что позволяет создавать афигенские типы данных включая, например, такую экзотику в БД как абстрактные классы (!)
  2. NoSQL с поддержкой SQL — вот такой вот хитрый и завораживающий микс
  3. поддержка ACID транзакций (вот Вам батенька и тупой NoSQL …)
  4. поддержка скриптовых языков на стороне клиента и сервера — из впечатляющих бонусов: Javascript (!).
  5. поддержка stored procedures, которые можно ваять на Javascript, Ruby, Scala, Java или печальном Groovy. Процедуры могут вызывать друг друга, поддерживаются рекурсия, вызов через REST, авто-маппинг параметров по имени и работа с обьектами от плагинов
  6. работа с деревьями, включая расширения SQL аля create vertex/edge и traverse с кипой примочек
  7. поддержка distributed и HA в ввиде master-master репликаций
  8. эффективная поддержка multi-tenancy и partitioned данных вплоть до уровня одной записи и легко вытекающая отсюда возможность использовать security at record level (horizontal security)
  9. open source, т.е. открыты исходники, протоколы и документация. Последняя в принципе далека от идеала, так что пользу придется извлекать в основном из первых двух источников
  10. неплохая скорость работы и отсутствие требований к гигабайтам свободной памяти как, к примеру, у БД от патентных троллей и скупщиков кр^H^H opensourc’а
  11. несмотря на то что сама база писана на джаве, имеются коннекторы под другие языки и довольно подробно описан бинарный протокол, зная который можно за короткий срок написать свой собственный коннектор под какой-нить новый супер-пупер язык (у меня ушло всего 2 дня на беглую реализацию варианта для C++). Есть коннекторы под PHP, NodeJS, etc. и наконец есть возможность работы просто через REST.

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

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

1. убеждаемся что в системе стоит jdk, если нет — качаем его с файлопомойки патентного тролля-неудачника

2. Набираем :

svn checkout http://orient.googlecode.com/svn/trunk/ orient-read-only
cd orient-read-only
ant installg

installg соберет и поставит в ../releases/orientdb-graphed-x.x.x-SNAPSHOT полную, так называемую graph версию базы, где есть поддержка tinkerpop blueprints и gremlin. Зачем оно надо прямо сейчас ? — хрен его знает, но все равно приятно, что оно на всякий случай есть под рукой ;)

3. Заходим в …/orientdb-graphed-x.x.x-SNAPSHOT/bin и запускаем server.sh При первом запуске в config файле БД автоматически сгенерятся пароли для админки сервера (root user password) и дефолтный юзер/пароль для вновь создаваемых БД (обычно user:admin, pass:admin). Как видим запуск этой БД очень прост даже для искушенных в своем невежестве юзверей. Конфиг лежит в …/orientdb-graphed-x.x.x-SNAPSHOT/config/orientdb-server-config.xml, там же можно разрешить запускать скрипты на сервере (для этого надо выставить параметр enabled в true для com.orientechnologies.orient.server.handler.OServerSideScriptInterpreter класса)

4. На другом терминале запускаем …/orientdb-graphed-x.x.x-SNAPSHOT/bin/console.sh и попадаем в командный монитор БД. Нужен он для выполнения базовых операций с БД, как-то: ее создание, администрирование, проверка запросов и т.д., все выделенные ниже команды запускаются именно из него.

Итак создаем базу :

create database remote:/test root root_pass local graph

Параметр remote говорит нам о том, что с базой будем работать по сети, тип storage local значит, что база будет располагаться на файловой системе сервера (можно указать тип memory — тогда база самоликвидируется при перезапуске сервера) и graph обозначает тип базы (graph или document). Первый тип используется когда нужна вся мощь работы с графами, второй имеет более простую и быструю их реализацию — связи могут быть только однонаправленными, не могут иметь properties, отключен blueprints и gremlin и т.д.

Также как и в обычных БД, OrientDB оперирует записями. Есть разные типы записей, но в основном используется понятие «документа» (document) — это запись, принадлежащая определенному классу и имеющая набор произвольных атрибутов.  Атрибуты иногда называют полями (fields) или свойствами (properties). Ну а что такое класс, должны хорошо знать те, кто читал Страуструпа первой редакции на ночь для достижения быстрого, крепкого и безмятежного сна.

Посмотреть какие классы есть у нас в базе можно командой classes, создать новый класс командой create class :

create class Contacts

Классы с натяжкой можно было бы приравнять к таблицам в обычной 2D БД, если бы не одно но:

insert into Contacts (name,surname) values (‘James’,'Bond’)

select * from Contacts

#RIDnamesurname
0#13:0JamesBond

Здесь мы можем наблюдать прикольное свойство OrientDB — не обязательно задавать тип и перечень полей записи при создании определенного класса или самой записи — это можно делать на лету (при вставке например). Но это еще не все — набор полей может быть разным/индивидуальным для каждой записи данного класса или произвольно меняться для любой записи в операциях insert/update.  Это свойство позволяет OrientDB гибко работать с данными, набор параметров которых, к примеру, изначально не известен или меняется по ходу работы приложения:

insert into Contacts (name,age) values (‘Batman’,30)
update Contacts set city = ‘Gotham’ where name = ‘Batman’

select * from Contacts where name = ‘Batman’

#RIDnameagecity
0#13:1Batman30Gotham

Ясен пень — работать в режиме стандартной БД, уныло вставляя все поля в соответствии с жесткой схемой, заданной для каждой записи тоже можно. Другая фишка OrientDB, которая прямо вытекает из вышеприведенной — это непосредственное управление (произвольный CRUD для произвольной записи) данными типа «associative array» или «map» внутри любой записи. Для этого команда UPDATE расширена подкомандами ADD (для массива) PUT (для map) и REMOVE для обоих типов :

update Contacts ADD tags = ‘superhero’
update Contacts PUT phones = ‘mobile’, ‘+007′ where name = ‘James’
update Contacts PUT phones = ‘office’, ‘+MI5′ where name = ‘James’
select * from Contacts where name = ‘James’

#RIDnamesurnametagsphones
0#13:0JamesBond[1]{mobile=+007, office=+MI5}

orientdb> display 0
—————————————————
ODocument — Class: contacts id: #13:0 v.3
—————————————————
name : James
surname : Bond
tags : [superhero]
phones : {mobile=+007, office=+MI5}

Ну а юзать массивы и maps внутри записей можно также, как это обычно и принято, конечно если БД такое умеет и позволяет. В случае с OrientDB это делается где-то так :

select phones from Contacts where phones.size() > 0
select phones from Contacts where phones.keys() in ‘mobile’

Продолжение впереди …