Несмотря на избитость и некую внешнюю пошарпанность темы, я все-таки подниму ее из пыли земной, слегка отряхнув, зажгу и воткну пылающим факелом знаний в темную ж… БД-невежества.
«Если в руке SQL база, то все вокруг кажется таблицами»
Народная программистская пословица
2D Тупик
И года не может пройти в нашем беспокойном мире без революций в той или иной сфере, конечно, не считая вечно кровавого политического болота. К счастью не политики, а именно окружающий мир (где они, к сожалению, тоже присутствуют) и определяет как будут выглядеть базы сегодняшнего и завтрашнего для. Наблюдается опеределенная тенденция — устаревшие реляционные 2D базы со своим псевдо-стандартным SQL отходят в прошлое, их место занимают более востребованные облачные БД с новыми возможностями и новыми языками запросов (NewSQL), а порой вовсе без них (NoSQL). Двухмерные таблицы старых БД не в состоянии обеспечить поддержку, по крайней мере, двух самых насущных парадигм, применяемых при решении современных социально-ориентированных задач, в которых первостепенную роль играют связи (виды связей) и/или отношения между обьектами. Первая парадигма — это иерархичность (трехмерность) классов обьектов и связей между ними (for ex. тот же социум со своими связями разного масштаба, производственные процессы, базы знаний и т.д.) Вторая — обьектно-ориентированная природа данных в задачах и, как следствие, желание естественным образом сохранить ее в БД. Увы, 2D базы никогда не были в состоянии хранить и более менее сносно осуществлять поиск в древовидных и графоподобных структурах данных, так как тяжеловесность доступных алгоритмов просто выходит за всякие рамки. Приведу яркий пример для MySQL (привет фэйлбук !) — табличка времени исполнения, на современном 4-ядерном лаптопе с 8GB RAM, запроса выдачи дерева друзей для всего 1000 юзеров при разной, и совершенно не впечатляющей глубине, (до 5) дерева [взято из книги "Neo4j in Action"] :
Глубина Время выполнения (секунд) для 1`000 юзверей Результат (записей)
2 0.028 ~900
3 0.213 ~999
4 10.273 ~999
5 92`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 по сравнению с общепринятыми решениями ?
Кратко :
- обьектная база по определению, что позволяет создавать афигенские типы данных включая, например, такую экзотику в БД как абстрактные классы (!)
- NoSQL с поддержкой SQL — вот такой вот хитрый и завораживающий микс
- поддержка ACID транзакций (вот Вам батенька и тупой NoSQL …)
- поддержка скриптовых языков на стороне клиента и сервера — из впечатляющих бонусов: Javascript (!).
- поддержка stored procedures, которые можно ваять на Javascript, Ruby, Scala, Java или печальном Groovy. Процедуры могут вызывать друг друга, поддерживаются рекурсия, вызов через REST, авто-маппинг параметров по имени и работа с обьектами от плагинов
- работа с деревьями, включая расширения SQL аля create vertex/edge и traverse с кипой примочек
- поддержка distributed и HA в ввиде master-master репликаций
- эффективная поддержка multi-tenancy и partitioned данных вплоть до уровня одной записи и легко вытекающая отсюда возможность использовать security at record level (horizontal security)
- open source, т.е. открыты исходники, протоколы и документация. Последняя в принципе далека от идеала, так что пользу придется извлекать в основном из первых двух источников
- неплохая скорость работы и отсутствие требований к гигабайтам свободной памяти как, к примеру, у БД от патентных троллей и скупщиков кр^H^H opensourc’а
- несмотря на то что сама база писана на джаве, имеются коннекторы под другие языки и довольно подробно описан бинарный протокол, зная который можно за короткий срок написать свой собственный коннектор под какой-нить новый супер-пупер язык (у меня ушло всего 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
# | RID | name | surname |
---|---|---|---|
0 | #13:0 | James | Bond |
Здесь мы можем наблюдать прикольное свойство 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’
# | RID | name | age | city |
---|---|---|---|---|
0 | #13:1 | Batman | 30 | Gotham |
Ясен пень — работать в режиме стандартной БД, уныло вставляя все поля в соответствии с жесткой схемой, заданной для каждой записи тоже можно. Другая фишка 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’
# | RID | name | surname | tags | phones |
---|---|---|---|---|---|
0 | #13:0 | James | Bond | [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’
Продолжение впереди …