Nicola Zunino

Lesezeit: 0 Minuten

Techblog

OrientDB: hybrides Datenbank-Modell im DeepDive, Teil 1

OrientDB ist eine multi-model Datenbank von Orient Technologies. Genauer gesagt handelt es sich um ein hybrides Model einer Datenbank, das verschiedene Funktionalitäten von NoSQL-Datenbank-Typologien zur Verfügung stellt. Nach einer kurzen Einführung in verschiedene Typen von Datenbank-Management-Systemen (DBMS) zeige ich ersten Kapitel Installation und erste Schritte mit OrientDB. Anschließend springe ich ins Arbeiten am konkreten Beispiel…

Techblog

OrientDB ist eine multi-model Datenbank von Orient Technologies. Genauer gesagt handelt es sich um ein hybrides Model einer Datenbank, das verschiedene Funktionalitäten von NoSQL-Datenbank-Typologien zur Verfügung stellt.

Nach einer kurzen Einführung in verschiedene Typen von Datenbank-Management-Systemen (DBMS) zeige ich ersten Kapitel Installation und erste Schritte mit OrientDB. Anschließend springe ich ins Arbeiten am konkreten Beispiel und zeige gängige DB-Aktionen in OrientDB.

  • Hier im zweiten Teil der Serie geht es weiter mit Insert, SELECT, UPDATE DELETE und Relationen (nein, kein JOIN ;-)).
  • In Teil drei werde ich mich mit Graphen Klassen, und SQL beschäftigen.
  • Teil vier dreht sich um Transaktionen, Nutzer und Rollen, verteilte Architekturen und APIs.

OrientDB und NoSQL

Obwohl das Konzept relationaler Datenbanken schon fast 50 Jahre alt ist – für unser Informatik-Umfeld eine Ewigkeit – , sind sie immer noch Basis vieler Anwendungen. Neuartige Anforderungen an Anwendungen wie Social Networks, Cloud-basierte Anwendungen, IoT-Plattformen und andere Fälle mit hohem Datentausch haben die Grenzen von RDBMS sehr klar geworden sind, insbesondere was Skalierbarkeit und Elastizität betrifft. Daher NoSQL. Und damit ist nicht “Kein SQL” gemeint, sondern “Not only SQL“. Ein NoSQL-DBMS will sich nicht ganz von SQL trennen, sondern es behält einige bis alle Funktionen und integriert weitere.

NoSQL-Vorteile

Nicht-relationale DBMS haben qua Anlage einige Vorteile:

  • Datenstruktur: NoSQL geht wesentlich weiter als relationale Datenbanken in der Datenverwaltung. Im NoSQL-Umfeld ist nicht die Struktur, sondern die Vielfalt der Daten Schwerpunkt. So ist es möglich, nicht-homogene Daten zu speichern, ohne Schnelligkeit im Zugriff beim Abfragen, Analysieren und Bearbeiten zu verlieren.
  • Skalierbarkeit: Im Gegensatz zu RDBMS basieren NoSQL-Datenbanken meistens auf dezidierten physikalischen Strukturen, die wesentlich besser auf mehrere Netzwerk-Knoten verteilt werden können (sharding).
  • Leistung: Die Skalierbarkeit erhöht die Leistung. Die Daten-Replikation an verschiedenen Stellen erlaubt eine schnellere Antwort an User-Abfragen.
  • Flexibilität: Bei einer NoSQL-Datenbank muss man nicht unbedingt eine starke Datentypisierung verwenden. Die Entwickler können sich so direkt im Code um Edge Cases kümmern.

NoSQL-Modelle in OrientDB

OrientDB bietet vier verschiedene Datenmodelle an, drei kommen direkt aus der NoSQL-Welt.

  • Sicher eines der wichtigsten Modelle, auch wenn es nicht aus der NoSQL-Welt stammt, ist das Object Model aus der objektorientierten Programmierung. Es ermöglicht die Definition von Klassen, unterstützt Vererbung und Polymorphie.
  • Das Document Model erlaubt dem Benutzer, Dokumente in sehr flexiblen Strukturen zu speichern (sogenannten Collections) und sie miteinander zu verlinken. In der NoSQL-Welt sind Dokumente Objekte, die aus Feldern bestehen. Diese Objekte müssen nicht unbedingt die gleiche Struktur haben, die Datenbank kann sie trotzdem in derselben Collection zu speichern. MongoDB ist das am weitesten verbreitete DBMS, das dieses Model unterstützt.
  • Das Graph Model speichert Dokumente in Knoten, die miteinander durch Relationen verbunden sind. Das entstehende Netz erlaubt, weitere Infos zu extrapolieren. Dadurch wird im NoSQL-Umfeld eine der flexibelsten Strukturen der Informatik dargestellt. Graphen sind die Basis von verschiedenen Algorithmen, sie basieren auf Knoten und Kanten: Knoten enthalten die Informationen und Kanten die Verbindungen.
  • Das Key/Value Model speichert Objekte in Strukturen, die durch Schlüssel indexiert sind. Diese Strukturen stammen von Maps oder Dictionaries, die Daten speichern, indem sie einen eindeutigen Key für jedes einzelne Objekt verwalten und dadurch schnelle Abfragen ermöglichen. Redis ist das bekannteste DBMS für ein Key/Value Model.

OrientDB bietet noch keine Unterstützung für ein Column-oriented Model, obwohl es in vielen bekannten DBMS wie Cassandra, Big Table oder SimpleDB verwendet wird, die von Social-Network-Giganten wie Facebook, Google und Amazon stammen.

Und trotzdem: Im Gegensatz zu anderen Beispiele ist OrientDB tatsächlich Multi-Model. Die vier genannten Typen sind nicht einfach verschiedene Schnittstellen des DBMS, sondern vier echte Modelle. Das ist einer der Gründe, wegen denen sich OrientDB etablieren konnte. Schließlich wissen wir mittlerweile: Kein NoSQL-Paradigma ist besser als das andere, bei allen gibt es Trade-offs und es gilt immer, die beste Lösung für die eigene Anforderung zu finden. Da OrientDB fast alle NoSQL-Modelle anbietet, eignet es sich als DBMS für viele Use Cases.

Erste Schritte

OrientDB Versionen

Wenn wir allgemein von OrientDB sprechen, meinen wir meistens die Produktfamilie mit zwei Versionen: Community und Enterprise. Die Community Edition ist ein Open-Source Projekt unter Apache-2.0-Lizenz. Die Enterprise Edition integriert dazu einen Query Profiler und unterstützt weitere Features wie Distributed Clustering oder Live Monitoring. OrientDB ist für MacOS, Linux und Windows erhältlich, hier geht’s zum Download.

Download und Installation

OrientDB braucht eine Java Virtual Machine (mindestens in Version 1.6). Für die Installation laden wir den Quellcode herunter oder besorgen den Quellcode direkt bei GitHub:

> git clone git@github.com:orientechnologies/orientdb.git
> cd orientdb
> ant clean install

Im Quellcode von OrientDB kann man sehen, dass es in verschiedene Ordner organisiert ist. Die wichtigsten sind:

  • bin: Der Ordner enthält alle Tools zur Verwaltung von OrientDB-Services. Zum Beispiel ein Skript um den DBMS zu starten, server.sh oder server.bat, und ein Client, console.sh oder console.bat.
  • databases: Hier werden alle erzeugten Datenbanken gespeichert. Jede bekommt einen eigenen Order. Als Trainings-Datenbank steht vom Anfang an GratefulDeadConcerts zur Verfügung.
  • config: Hier sind alle Config-Dateien in XML-Format gespeichert, zum Beispiel orientdb-server-config.xml, um die User zu speichern.
  • www: Hier ist der Webclient zu finden.

Server starten

Um einen OrientDB-Server zu starten, gibt man im bin-Ordner Folgendes ein:

> ./server.bat

Der Server läuft und man kann durch eine Konsole interagieren. Die Konsole wird gestartet durch:

> ./console.bat

Um mit einer Datenbank zu interagieren, ist es nötig, sich mit dem Server zu verbinden. Bevor es aber möglich ist, muss man einen Benutzer erstellen. Dafür wird die orientdb-server-config.xml Datei benötigt, die im config-Ordner zu finden ist. Im XML gibt es ein Users-Element, es kann wie folgt modifiziert werden:

<users>
  <user resources="*" password="my_pass" name="root"/>
  <user resources="connect,server.listDatabases,server.dblist" password="guest" name="guest"/>
</users>
 

Danach ist es möglich, sich mit de, Server zu verbinden:

> connect remote:localhost root my_pass

Jetzt kann man zum Beispiel mit list databasesalle Datenbanken auflisten. help steht ebenfalls zur Verfügung.

Eine Datenbank erstellen

Eine neue Datenbank wird durch create database erzeugt. Für jede Datenbank muss man einen storage type auswählen. Möglich sind:

  • plocal: Die Datenbank wird im Dateisystem gespeichert. Um zum Beispiel eine mydb-Datenbank im /tmp-Ordner zu speichern, wird plocal:/tmp/mydb eingegeben. > create database plocal:/home/admin/databases/mydb
  • remote: Für Datenbanken, die sich im Netz befinden, wird der Name nach einer IP-Adresse eingegeben. Um zum Beispiel mydb auf localhost zu generieren, wird remote:localhost/mydb eingegeben. > create database remote:localhost/mydb
  • memory: Die Datenbank ist im Hauptspeicher. > create database memory:mydb

Außerdem kann man sofort einen Benutzername und ein Passwort setzen. In dem Fall muss man zwingend einen Storage Type zu setzen, entweder plocal oder memory.

> create database remote:localhost/newdb root myuser plocal

Falls keine Benutzer explizit definiert sind, ist es möglich, einen der drei vordefinierten Benutzer zu verwenden. In dem Falls stimmen die Passwörter mit den Benutzernamen überein:

  • reader
  • writer
  • admin

Datenstruktur

Unabhängig vom Modell gibt es drei Konzepte, die die Basis für das Datenspeichern sind: Class, Record und Cluster.

Records und Klassen

Fürs Design einer Datenbank wird normalerweise ein Set von Tabellen definiert, und für jede Tabelle wird die Struktur festgelegt. Das bedeutet, für jede Spalte werden ein Datumstyp und ein Range festgestellt. Erst dann können Zeilen hinzu gefügt werden. Jede Zeile muss dabei die Spaltenstruktur und eventuelle Constraints respektieren.

In OrientDB ist der Record die Basis für alles. Ein Record ist eine Aggregation von Daten, die einzeln zu Spalten geordnet werden. Mehrere Records werden in Cluster zusammengeordnet, also eine Struktur, die der Tabelle in relationalen Datenbanken entspricht. Jeder Record hat eine Record ID, die aus zwei Teil zusammengesetzt wird: cluster-id:cluster-position. Cluster-id sagt, zu welchem Cluster der Record gehört, cluster-position beschreibt die Position des Records im Cluster. Die Record ID ist eine Pflichtangabe und wird automatisch generiert.

Bei den Records sieht man einen der größten Unterschiede zu relationalen Datenbanken: Die Datenstruktur ist nicht vorgeschrieben. Jeder Record muss einer bestimmten Klasse zugeordnet sein, aber Klassen müssen keine interne Struktur definieren. Falls doch eine interne Struktur definiert sein sollte, kann sie so flexibel sein, wie es für den einzelnen Record nötig ist. Es gibt drei verschiedene Möglichkeiten, um eine Klasse zu definieren, abhängig davon, wie die Datenstruktur benutzt wird:

  • schema-less: Die Klasse wird ohne interne Struktur definiert. Jeder einzelner Record wird dann eine eigene Struktur haben.
  • schema-full: Die Klasse wird definiert, ebenso alle ihre Eigenschaften wie Datentypen, Range usw. Alle Records werden dieselbe Struktur haben, und es gibt keine Möglichkeit abzuweichen. Hier bietet sich der strict-mode an.
  • schema-hybrid oder schema-mixed: Die Klasse wird definiert und dabei werden auch die Basis-Eigenschaften angelegt. Dabei bleibt die Möglichkeit offen, dass jeder Record die interne Struktur individuell setzt.

Cluster

Klassen sind Konzepte, die Information beschreiben sollten. Die tatsächlichen Daten werden in Cluster gespeichert, die Tabellen ähneln. Um Records zu speichern, die zu einer Klasse gehören, muss die Klasse mindestens mit einem Cluster in Verbindung stehen.

Ein Beispiel: Um Kundendaten zu speichern, wird Klasse Customer definiert. Alle Records dieser Klasse dürften zu mehr als einem Cluster gehören, zum Beispiel nach Herkunftsland (USA_customers, Euro_customers, China_customers). In diesem Fall wird ein Default Cluster definiert, das durch ein * identifiziert wird (zum Beispiel Euro_customers*). Alle Inserts, Updates oder Queries müssen auf eine Klasse bzw. einen Cluster gemacht werden. Falls es mehrere Cluster für eine Klasse gibt, wird bei einem Insert der Default Cluster verwendet. Bei einer Suche werden alle Cluster durchsucht, die der Klasse zugeordnet sind.

Die Partitionierung von Records auf Clustern steigert die Performance:

  • Falls man den Cluster kennt, wo ein Record gesucht werden muss, kann man direkt auf den richtigen Cluster zugreifen; so braucht es weniger Indexierung.
  • Daten zu teilen, macht Sharding (die Datenteilung im Netz) einfacher.

Cluster können physisch gespeichert werden (Physical cluster) oder In-Memory sein (Memory cluster). Als Default sind Cluster physisch und werden im Dateisystem gespeichert. In-Memory Cluster werden am Ende des darauf arbeitenden Prozesses gelöscht.

SQL in OrientDB

SQL ist in OrientDB so ähnlich wie möglich zu Standard-SQL implementiert. Trotzdem bleiben einige große Unterschiede.

  1. Es gibt keine JOIN zwischen Tabellen. Statt dessen werden links zum Verbinden der verschiedenen Entities verwendet. Außerdem ist es möglich, Daten ohne vertikale Projektion zu suchen, also statt
SELECT * FROM ...
 

geht es nun elegant so

SELECT FROM ...
 
  1. In OrientDB gibt es keine HAVING -Klausel, statt dessen kommen Subqueries zum Einsatz
  2. DISTINCT funktioniert ebenfalls anders – dazu komme ich weiter unten.

SQL Beispiel

Eine Datenbank erzeugen

OrientDB SQL unterstützt CRUD. Zu Beginn braucht man dieses Setup:

  • eine laufende OrientDB-Instanz: also server.sh (server.bat in Windows) starten, befindet sich im bin Ordner;
  • einen Client, um Queries zu starten. Dafür gibt es zum Beispiel eine Text-Konsole, sie wird durch das Script bin/console.sh hochgefahren.
  • eine Datenbank:CREATE DATABASE remote:localhost/testsql root root plocal  
  • eine Klasse im schema-less Modus:CREATE CLASS Customer  

Erste Schritte

Als erstes werden Daten hinzugefügt

INSERT INTO Customer(name, surname) VALUES ('Paul', 'Smith')
 

Es wird ein Record erzeugt, der automatisch aus zwei Attributen besteht und einen Record-Id hat (zum Beispiel #11:0, falls 11 die Cluster-Id ist)

So lassen sich weitere Records hinzufügen:

INSERT INTO Customer(name, surname, age) VALUES ('John', 'White', 51)
INSERT INTO Customer(name, surname, city) VALUES ('Helen', 'Green', 'New York')
 

Jedes INSERT fügt einen neuen Record hinzu. Jeder Record hat eine eigene Record-ID. Folgende Query zeigt die Situation:

SELECT FROM Customer
 
----+-----+--------+------+-------+----+--------
#   |@RID |@CLASS  |name  |surname|age |city
----+-----+--------+------+-------+----+--------
0   |#11:0|Customer|Paul  |Smith  |null|null
1   |#11:1|Customer|John  |White  |51  |null
2   |#11:2|Customer|Helen |Green  |null|New York
----+-----+--------+------+-------+----+--------

Folgendes fällt dabei auf:

  • Es wurde keine Datenstruktur definiert, sondern nur eine logische Entität (die Klasse), die aber keine Eigenschaften hat. Die Klasse hat automatisch ein Cluster definiert.
  • Alle Records haben eine verschiedene interne Struktur;
  • Jeder Record hat eine eigene Nummer, die mit dem Cluster zusammen das RID definiert. Das ist der einzige WEg, um den Record identifizieren zu können.

Es ist möglich den RID in einer WHERE-Klausel zu verwenden:

SELECT FROM Customer WHERE @rid=#11:1
 
----+-----+--------+------+-------+----
#   |@RID |@CLASS  |name  |surname|age
----+-----+--------+------+-------+----
0   |#11:1|Customer|John  |White  |51
----+-----+--------+------+-------+----

Wie im Standard-SQL kann man auch diese Query schreiben:

SELECT FROM Customer WHERE surname='Smith'
 
----+-----+--------+-------+-----
#   |@RID |@CLASS  |surname|name
----+-----+--------+-------+-----
0   |#11:0|Customer|Smith  |Paul
----+-----+--------+-------+-----

Auch DELETE und UPDATE stehen zur Verfügung. Falls man zum Beispiel die Stadt von New York auf Los Angeles ändern möchte:

UPDATE Customer SET city='Los Angeles' WHERE city='New York'
 

Löschen geht so:

DELETE FROM Customer WHERE surname='White'
 

Umgang mit Klassen

Klassen definieren

Man möchte, dass die Customer Klasse drei Eigenschaften hat, zum Beispiel name, surname und age, und die beiden ersten zwei sind String und die dritte eine Zahl:

CREATE CLASS Customer
CREATE PROPERTY Customer.name String
CREATE PROPERTY Customer.surname String
CREATE PROPERTY Customer.age Short
 

Mit info class kann die Struktur einer Klasse angezeigt werden:

> INFO CLASS Customer


Class................: Customer
Default cluster......: customer(id=11)
Supported cluster ids: [11]
Cluster selection....: round-robin
PROPERTIES
+----------+-----------+-----------+----------+
 NAME                          | TYPE
-------------------------------+---------------
 age                           | SHORT
 name                          | STRING
 surname                       | STRING

Um Daten zu speichern, kann man Folgendes schreiben:

INSERT INTO Customer(name, surname, age) VALUES ('Carl', 'Black', 41)
INSERT INTO Customer(name, surname, age, info) VALUES ('Carl', 'Black', 41, 'something')
 

Das erste INSERT passt genau zur Klassenstruktur. Die zweite erzeugt eine neue Eigenschaft (mit Name info): Da es keine feste Struktur gibt, ist diese Vorgehensweise möglich. Wenn der Datentyp eine definierte Eigenschaft nicht respektiert, wie in diesem Beispiel, bekommt man einen Fehler (Fehler hier: age ist eine Zahl):

INSERT INTO Customer(name, surname, age, info) VALUES ('Carl', 'Black', 'Forthyone', 'something')
 

Mit BROWSE CLASS ist es möglich, den Inhalt einer Klasse zu sehen:

> BROWSE CLASS Customer
----+-----+--------+-----+-------+----+-----------------
#   |@RID |@CLASS  |name |surname|age |info
----+-----+--------+-----+-------+----+-----------------
0   |#11:7|Customer|Carl |Black  |41  |null
1   |#11:8|Customer|Carl |Black  |41  |something
----+-----+--------+-----+-------+----+-----------------

Mit LIST CLASSES oder einfach CLASSES kann man alle Klassen anzeigen.

Klassen ändern und löschen

Mit DROP CLASS ist es möglich Klassen zu löschen. Um zum Beispiel Customer zu löschen, schreibt man:

DROP CLASS Customer
 

Falls man eine Eigenschaft ändern möchte, wird ALTER CLASS verwendet:

ALTER CLASS class-name attribute-name attribute-property
 

Die Klasse Customer benennt man so in Buyer um:

ALTER CLASS Customer NAME Buyer
 

Mit ALTER CLASS ist es möglich, auf den strict mode zuzugreifen. Wenn zum Beispiel die Klasse Customer von schema-mixed (d.h. einige Eigenschaften hat man definiert, und man kann andere Eigenschaften hinzugefügen, wenn nötig), schema-full Modus setzen möchte, geht man so vor:

ALTER CLASS Customer STRICTMODE TRUE
 

In diesem Fall würde eine INSERT würde nur definierte Eigenschaften akzeptieren und ansonsten einen Fehler werfen. Das war nur ein Ausschnitt. Wie man weitere Attribute ändert, ist in der offiziellen Doku zu finden.

Cluster benutzen

Um Records zu speichern, benötigt man mindestens einen cluster als physische Struktur der Datenbank. Mit info class wird auch die IDs der Cluster angezeigt, in denen die Daten einer Klasse gespeichert werden. Man kann einer Klasse weitere Cluster ALTER CLASS zuordnen:

ALTER CLASS Customer ADDCLUSTER newlist
 

Falls es noch keinen Cluster mit dem Namen geben sollte, wird es erzeugt und seine ID als Output angezeigt. Cluster können explizit mit CREATE CLUSTER erzeugt werden:

CREATE CLUSTER newlist
 

Falls eine Klasse zu mehreren Cluster zugeordnet sein sollte, gibt man an, wo neue Records gespeichert werden müssen:

INSERT INTO cluster:newlist(name, surname) VALUES ('Sammy', 'Colton')
 
> BROWSE CLASS Customer
----+-----+--------+-----+-------+----+-----------------
#   |@RID |@CLASS  |name |surname|age |info
----+-----+--------+-----+-------+----+-----------------
0   |#11:7|Customer|Carl |Black  |41  |null
1   |#11:8|Customer|Carl |Black  |41  |something
2   |#13:0|Customer|Sammy|Colton |null|null
----+-----+--------+-----+-------+----+-----------------

Hier sieht man deutlich, dass die ID des letzten Record sich von den beiden Ersten unterscheidet: @RID fängt mit #13 an, die Anderen mit #11, weil sie in einem anderen Cluster sind.

Es ist möglich nur einen Cluster zu sehen:

BROWSE CLUSTER newlist
 

oder

SELECT FROM cluster:newlist
 
----+-----+--------+-----+-------
#   |@RID |@CLASS  |name |surname
----+-----+--------+-----+-------
0   |#13:0|Customer|Sammy|Colton
----+-----+--------+-----+-------

Im nächsten Artikel beschäftige ich mich mit Insert, SELECT, UPDATE DELETE und Relationen. Hier geht es zu Teil zwei.

Über den Autor

Nicola Zunino

WE, Web Engineering

Nicola arbeitet seit 2017 bei MaibornWolff. Zuerst war er als Frontend Entwickler tätig, mit Schwerpunkte JavaScript und Frameworks wie Angular und React, entdeckte dann jedoch, dass ihm Architekturen und Analysen, also designen, mehr Spaß macht. Als ehemaliger Dozent, unterstützt er interne Schulungen (Clean Code) und Entwicklung der Kollegen. Als Doktor Ingenieur hat er sich in Daten Optimierung und Datenbanken spezialisiert: Datenanalyse, Pattern Matching und Clustering sind noch seine Leidenschaften. 

LinkedIn: https://www.linkedin.com/in/nicola-zunino-22852037/, Twitter: @NicolaZStrong, Instagram: @nicola_zunino, nicola.zunino@maibornwolff.de