NoSQL für anspruchsvolle Datenbanklösungen

Sag ja zu NoSQL für anspruchsvolle Datenbanklösungen
Keine Kommentare

Wer heute eine datenbankgetriebene Applikation erzeugt, greift meist zu einer SQL-Datenbank. Dies funktioniert anfangs gut, die Abfragesprache ist nahezu jedem Entwickler zumindest peripher geläufig. Haarig wird es, wenn die Menge der zu verarbeitenden Daten oder die Konsistenzsansprüche ansteigen. In diesem Fall schaffen NoSQL-Datenbanksysteme Abhilfe. Ihre Nutzung setzt – je nach verwendetem Modell – mehr oder weniger Umdenken auf Seiten des Entwicklers voraus: Eine NoSQL-Datenbank lässt sich nicht per „Cut and Paste“ in eine existierende Lösung integrieren.

Gleich zu Beginn: Es ist unmöglich, in einem einzelnen Artikel eine komplette Übersicht über die Welt des NoSQL zu geben. Unter dem Sammelbegriff tummelt sich eine Vielzahl verschiedener Ansätze: Vom KV-Speicher bis zu immens komplexen Datenbanksystemen gibt es so gut wie nichts, was nicht vertreten ist. Manche Autoren bieten tausendseitige Lehrbücher an, die nur an der Oberfläche des Themas kratzen.

Für die folgenden Schritte wollen wir uns auf eine vergleichsweise einfache Definition einigen: Eine NoSQL-Datenbank ist ein Datenbanksystem, das die Interaktion zwischen Applikation und im Remanentspeicher befindlichen Informationen unter einer Abfragesprache abwickelt, die nicht auf SQL basiert. Dass es hierbei oft textuelle Sprachen gibt, die SQL mehr oder weniger stark ähnlich sind, folgt aus der Logik. Das von MySQL bzw. Qt verwendete Modell der dualen Lizenzierung findet sich auch in der Welt der NoSQL-Datenbanken. In ziemlich allen Fällen gibt es eine kostenlose und quelloffene Basisversion der Datenbank. Wer allerdings fortgeschrittene Funktionen wie beispielsweise komplexe Skalierungen oder Hochverfügbarkeitsfunktionen nutzen möchte, ist meist dazu verpflichtet, eine kommerzielle Lizenz zu lösen. Die dabei anfallenden Preise unterscheiden sich von Anbieter zu Anbieter, sollten aber auf keinen Fall unterschätzt werden.

Mit der Kraft des Dokuments!

Wer eine SQL-basierte Datenbankapplikation realisiert, beginnt mit der Definition des Datenbankschemas. Es handelt sich dabei um eine Beschreibung, die festlegt, in welchem Format die von der Datenbank zu verarbeitenden Daten vorliegen werden. Diese Vorgehensweise erweist sich in der Praxis insofern als „bissig“, als man bei der Deklaration des Schemas das Problem hat, dass man nicht alle Bedürfnisse von vornherein absehen kann. Ein schönes Beispiel dafür wäre eine Datenbank, die Oszillografen und ihre Eingangskanäle verwaltet.

Bis zum Erscheinen von Danahers Achtkanal-Oszilloskopen – wir wollen Yokogawas nur im Energietechnikbereich relevanten Geräte hier ignorieren – hätte es ausgereicht, ein Schema anzulegen, das für vier Kanäle Informationen vorhält. Muss man einen Zweikanaloszilloskopen speichern, so lässt man die beiden Felder für die Kanäle drei und vier leer. Seit der Einführung des Achtkanälers muss man in diesem Fall sechs Felder freilassen, beim Vierkanäler schleppt die Datenbank vier unbenötigte Felder mit. Dieses in kleinen Datenbanken unproblematische Vorgehen erweist sich spätestens dann als kritisch, wenn Sie statt einiger Dutzende einige Millionen messtechnischer Geräte zu verwalten haben. Ein Weg zur Umgehung dieses Problems ist die dokumentenorientierte Datenbank, die in Abbildung 1 schematisch dargestellt ist.

Abb. 1: Dokumentenorientierte Datenbanken arbeiten mit als Dokumenten bezeichneten KV-Speichern

Die genaue Aufteilung der Datenbank ist von Implementierung zu Implementierung unterschiedlich: Wir wollen in den folgenden Schritten, schon aufgrund der weiten Verbreitung, auf MongoDB setzen. Allen Datenbanken gemeinsam ist, dass Dokumente eine datenbankweit einzigartige ID aufweisen, über die sie während ihres gesamten Lebenszyklus ansprechbar bleiben. Die eigentliche Struktur ist derweil flexibel: In manchen Implementierungen dürfen Sie als Entwickler immerhin einige Felder als immer vorhanden bzw. erforderlich deklarieren, während Ihnen andere Produkte auch diese Freiheit nicht zugestehen.

Da Linux bzw. unixoide Betriebssysteme in der Welt der NoSQL-Datenbanken die Rolle eines Quasistandards einnehmen, wollen wir in den folgenden Schritten mit Ubuntu 14.04 arbeiten. Die Installation der Datenbank erfolgt durch Eingabe von sudo apt-get install mongodb.

Interessant ist, dass das MongoDB-Team mit Ubuntu nicht komplett d’accord geht. Laut der unter https://docs.mongodb.com/manual/tutorial/install-mongodb-on-ubuntu/ bereitstehenden Dokumentation ist das soeben installierte MongoDB-Paket kein offizielles Kompilat der MongoDB-Gruppe, sondern wird von Ubuntu selbst verwaltet. Derartige Spaltereien trifft man in der Welt der NoSQL-Datenbanken häufiger, wir wollen an dieser Stelle allerdings nur darüber lächeln und uns sonst nicht weiter damit aufhalten. Öffnen Sie stattdessen durch Eingabe von Mongo die Konsole der Datenbank, die sich wie in Abbildung 2 gezeigt präsentieren wird. In der Praxis wird er eher für Wartungsaufgaben genutzt, doch für erste Experimente ist er ideal geeignet.

Abb. 2: Der Kommandozeilenclient ermöglicht uns die unbürokratische Interaktion mit der Datenbank

MongoDB unterscheidet sich von anderen Datenbanken insofern, als eine neue Datenbankinstanz, also eine Gruppe von Dokumenten, nicht explizit angemeldet werden muss. Stattdessen reicht es aus, die in Listing 1 dargestellten Befehle einzugeben.

db.sus.insert({
  name: 'DS-6612',
  maker: 'Iwatsu',
  chcount: '2',
  ch1:'60',
  ch2:'60'
});
db.sus.insert({
  name: '9354AM',
  maker: 'LeCroy',
  chcount: '4',
  ch1:'500',
  ch2:'500',
  ch3:'500',
  ch4:'500'
});

db dient hierbei als Mater-Objekt, das alle in der MongoDB-Instanz vorliegenden Datenbanken zusammenfasst. Der Insert-Befehl nimmt sodann ein JSON-Array entgegen, das die eigentlichen in der Datenbank zu platzierenden Informationen bereitstellt. Schon hier fällt auf, dass die Anmeldung des Zweikanal- und des Vierkanaloszillografen im Großen und Ganzen identisch erfolgt: Der einzige Unterschied ist, dass der Vierkanäler eben zusätzliche Informationen mitbringt. MongoDB setzt an dieser Stelle keine besonderen Operationen voraus – wer ein zusätzliches Feld anlegen möchte, liefert es einfach an.

Tiefergehende Analyse

Zur Anlieferung aller in der Datenbank befindlichen Tupel reicht es aus, den Befehl db.sus.find() einzugeben. Die Datenbank reagiert darauf mit der in Abbildung 3 gezeigten Ausgabe. Besonders interessant ist, dass jeder Oszillograf ein Feld namens Id eingeschrieben bekam, das die in der Einleitung zu diesem Abschnitt angegebene Objekt-ID enthält. MongoDB generiert diese zur Laufzeit automatisch und stellt über verschiedene Algorithmen sicher, dass sie einzigartig ist.

Abb. 3: Die beiden Oszillografen sind in der Datenbank angekommen

Wirklich interessant wird MongoDB durch die Fähigkeit, an SQL erinnernde Abfragen gegen den Datenbestand durchzuführen. Wollen wir beispielsweise nach Oszillografen suchen, die zumindest vier Kanäle haben, bietet sich der Befehl db.sus.find({ch4:{$exists: true}}); an.

ch4 ist dabei das eigentlich zu prüfende Feld, dem statt einem gewöhnlichen Matcher-Wert ein weiteres JSON-Objekt eingeschrieben wird. Das Makro $exists informiert MongoDB darüber, dass wir die Existenz des jeweiligen Felds überprüfen – wer die vorliegende Anfrage gegen unsere zwei Oszilloskope ausführt, stellt fest, dass MongoDB nur den LeCroy als Antwort zurückgibt. Das $ dient in der MongoDB-Welt übrigens allgemein als Befehlsoperator, der die Datenbank darauf hinweist, dass es an dieser Stelle eine besondere Aufgaben zu tun gibt.

Wie dem auch sei, ist das manuelle Vergeben von Namen für die einzelnen Kanäle alles andere als bequem. Würden wir beispielsweise einen Yokogawa mit seinen acht Kanälen in die Datenbank einfliegen wollen, müssen wir die Strings von CH1 bis CH8 von Hand eintippen. Deren Erzeugung artet – auch programmatisch – in Arbeit aus und ist entsprechend lästig. Zudem besteht das Risiko von Tippfehlern, die die Datenbank ob des Fehlens eines Schemas nicht detektieren kann.

Als Alternative bietet MongoDB die Nutzung von Arrays an. Es handelt sich dabei um Objekte, die mit den von MySQL und Co bekannten Blobs verwandt sind. Der wichtigste Unterschied zu einem Blob ist, dass die in einem RE enthaltenen Elemente aus Sicht von MongoDB ebenfalls „Kinder“ des Dokuments sind und entsprechend gültige Ziele für Abfragen darstellen. Zur besseren Nutzung dieser Funktion müssen wir an den Start zurückkehren: Wir gingen davon aus, dass db.sus den Namen einer Datenbank beschreibt. Das ist nicht wahr – nach dem Start wählt der MongoDB-Client vielmehr eine Datenbank aus, deren Namen sich folgendermaßen ermitteln lässt:

> db.getName()
test

Interessant ist an dieser Stelle die Ermittlung aller Datenbanken, die von der MongoDB-Instanz verwaltet werden. Hierzu kommt der Befehl adminCommand zum Einsatz, der Kommandos gegen die Verwaltungsinstanz des Clusters ausführt:

> db.adminCommand( { listDatabases: 1 } )

Der String .sus steht für ein als Collection bezeichnetes Hilfskonstrukt. MongoDB-Datenbanken können die in ihnen enthaltenen Objekte gruppieren – im Fall unserer Datenbank für Messgeräte wäre es beispielsweise denkbar, in eine Gruppe für Multimeter, eine Gruppe für Oszilloskope und eine Gruppe für Spektralanalysatoren und andere RF-Sachen anzulegen. Diese auf den ersten Blick pedantische Vorgehensweise ist in der Praxis wertvoll, weil sie die zur Bearbeitung der verschiedenen Anfragen erforderlichen Rechenleistung minimiert. Wie dem auch sei, legen wir im nächsten Schritt eine Gruppe von Oszilloskopen mit Array durch Eingabe des folgenden Befehls an:

db.susnew.insert({
name: 'DS-6612',
maker: 'Iwatsu',
channels:['60', '60']
});
db.susnew.insert({
name: '9354AM',
maker: 'LeCroy',
channels:['500', '500', '500', '500']
});

Dieses Kommando unterscheidet sich an zweierlei Punkten vom Vorgänger: Erstens nutzen wir nun die Collection susnew. Zweitens geben wir die einzelnen Kanäle nicht mehr explizit an, sondern nutzen stattdessen ein Array mit ihren jeweiligen Bandbreitenwerten. Das erfolgreiche Einpflegen der Werte lässt sich abermals durch einen Find-Befehl überprüfen, der nun allerdings gegen die neue Kollektion angewandt werden muss:

> db.susnew.find()
{ "_id" : ObjectId("5ab83a1c75fc1f9579af46e8"), "name" : "DS-6612", "maker" : "Iwatsu", "channels" : [  "60",  "60" ] }
. . .

Von Interesse ist hier die Möglichkeit, über die weiter oben besprochenen Operatoren Veränderungen an Feldern vorzunehmen. Als Beispiel hierfür wollen wir uns den Push-Operator ansehen, der ein neues Element in das Feld einfügt:

db.susnew.update(
{ "_id" : ObjectId("5ab83a1c75fc1f9579af46e8")  },
{ "$push" : { "channels" :  "50"}  }
);

Push ist in MongoDB als unbedingtes Hinzufügen eines Werts definiert: Befindet sich ein identischer Wert bereits im Feld, wird dies von der Datenbank ignoriert. Wer stattdessen $addToSet übergibt, weist den Datenbankserver dazu an, das jeweilige Feld als Sammlung zu betrachten, in der jedes Element nur ein einziges Mal vorkommen darf. Ist der angegebene Wert schon im Feld, verfällt dieser Teil des update-Befehls wirkungslos. Zur Suche nach Vierkanaloszillografen reicht es dann aus, $size zu übergeben:

db.susnew.find(
{ "channels" : { $size :  4}  }
);

Beachten Sie, dass MongoDB das Makro streng ausgelegt. Wer als Parameter beispielsweise vier übergibt, bekommt nur jene Dokumente zurückgeliefert, deren Feld auch wirklich genau vier Elemente aufweist. Für komplexere Vergleiche stehen in der Abfragesyntax andere Sprachelemente zur Verfügung, auf die wir an dieser Stelle schon aus Platzgründen nicht weiter eingehen können. Unter https://docs.mongodb.com/manual/ findet sich allerdings eine lesenswerte Dokumentation, die die Abfragesprache im Detail erklärt und auch als eine Art Einführung in den (beeindruckenden) Funktionsumfang dieses dokumentorientierten Datenbanksystems dienen kann.

Dokumentendatenbank ohne Dokumente

MongoDB erinnert Personen, die mit Plattformen wie Samsungs bada aufgewachsen sind, instinktiv an den KV-Speicher. Diese Assoziation ist nicht unbedingt falsch, ist ein KV-Speicher doch im Grunde genommen ein einzelnes Dokument in einer Kollektion einer MongoDB-Datenbank. In vielen Fällen reicht es völlig aus, einen KV-Speicher anzulegen: Ein schönes Beispiel hierfür wäre beispielsweise die Verwaltung von Kundeninformationen, bei denen die Einzigartigkeit der Kundennummer schon auf eine andere Art und Weise sichergestellt ist. Ein weiterer Vorteil von KV-Speichern ist ihre Primitivität, die Skalierung und Co. erleichtern.

Riak hat sich in der letzten Zeit als Quasistandard etabliert: was einst als einfache KV-Implementierung begann, unterstützt mittlerweile Clustering, Skalierung und eine Vielzahl anderer fortgeschrittener Spielereien. Wer Riak unter Ubuntu 14.04 verwenden möchte, muss die folgenden Befehle eingeben:

curl -s https://packagecloud.io/install/repositories/basho/riak/script.deb.sh | sudo bash
sudo apt-get install riak=2.2.3-1

Riak ist zum Zeitpunkt der Drucklegung dieses Artikels noch nicht Teil der offiziellen Paketquellen und dürfte dies auf absehbare Zeit auch nicht werden. Das Entwicklerteam stellt allerdings ein Skript bereit, das die Integration des hauseigenen Repositories bereitstellt bzw. erleichtert – das eigentliche Deployment erledigt dann das hinreichend bekannte apt-get install.

Danach lässt sich der Server auch schon durch Eingabe des folgenden Kommandos anwerfen – ignorieren Sie die Fehlermeldung für den Testbetrieb:

tamhan@TAMHAN14:~$ sudo riak start
!!!!
!!!! WARNING: ulimit -n is 1024; 65536 is the recommended minimum.
!!!!
tamhan@TAMHAN14:~$
Von der Fork-Bombe
Ein klassischer DoS-Angriff auf unixoide Betriebssysteme besteht darin, ein Programm loszulassen, das eine Vielzahl von Prozessen spawnt. Zur Einschränkung dieses Risikos steht mit ulimit ein Kommando zur Verfügung. Es ist unter https://www.networkworld.com/article/2693414/operating-systems/setting-limits-with-ulimit.html im Detail beschrieben. Wer seine RIAK-Installation im praktischen Einsatz verwenden möchte, sollte darauf achten, die Grenzwerte ausreichend hoch festzulegen.

Bei den Ausführungen zu MongoDB hatten wir auf den hauseigenen Kommandozeilenclient gesetzt: So gut wie alle Datenbanken bringen ein derartiges Werkzeug oder eine auf eine REST basierende Programmierschnittstelle mit. In der Praxis ist deren Nutzung oft nicht wünschenswert: Es ist viel vernünftiger, die Erstellung eines Wrappers für die jeweilige Programmiersprache an Dritte auszulagern. Wer eine Suchmaschine mit dem String „Programmiersprache Datenbank“ füttert, findet so gut wie immer eine Lösung. Im Fall von Riak wollen wir in den folgenden Schritten auf den offiziellen Client setzen. Wer sich das Herumspielen mit Maven ersparen möchte, findet unter http://riak-java-client.s3.amazonaws.com/index.html eine Liste von .jar-Dateien. Zum Zeitpunkt der Drucklegung ist die aktuellste Version aus dem Dezember 2016 und hört auf den Namen riak-client-2.1.1-jar-with-dependencies.jar – integrieren Sie sie in ein Java-Programm Ihrer Wahl, um mit den Spielen zu beginnen.

Das Riak-API erweist sich insofern als haarig, als ein Gutteil der in ihm enthaltenen Klassen unter identischen Namen auch in Paketen der Java-Standardbibliothek oder in Android vorkommt. Achten Sie aus diesem Grund immer darauf, beim automatischen Erzeugen der Imports in der IDE Ihrer Wahl in den Riak-Paketen befindliche Versionen der verschiedenen Codestrukturen auszuwählen. Besonders lustig ist in diesem Zusammenhang der Option-Typ, der für jedes spezifische Kommando individuell ist. Da es mehrere Operationen gibt, ist es empfehlenswert, zu seiner Einbindung statt auf einen Importbefehl auf die hier gezeigte vollständige Qualifikation der jeweiligen Klasse zu setzen. Ein erstes Programmbeispiel präsentiert sich dann folgendermaßen:

public static void main(String[] args) {
  try {
    RiakClient client =   RiakClient.newClient("127.0.0.1");
    Namespace ns = new Namespace("default", "ein_bucket");
    Location location = new Location(ns, "ein_key");
    RiakObject riakO = new RiakObject();
    riakO.setValue(BinaryValue.create("value"));
    StoreValue store = new StoreValue.Builder(riakO)
      .withLocation(location)
      .withOption(com.basho.riak.client.api.commands.kv.StoreValue.Option.W, new Quorum(3)).build();
    client.execute(store);
  }
}

Riak orientiert den inneren Aufbau der Datenbank anhand von HTTP. Es ist somit nicht verwunderlich, dass ein Namespace und eine Location zur Beschreibung eines Elements erforderlich sind. Die beiden Objekte würden sich später auch im Rest-API wiederfinden, wo sie das Ansprechen ermöglichen. Die Nutzung der Value-Objekte ist eine weitere Anleihe an REST: Sie legen fest, welche Art von Transaktion durchzuführen ist. Wir nutzen hier einen StoreValue, der Werte in den Speicher schreibt. Wer das Objekt FetchValue verwendet, liest Informationen aus der Datenbank.

Aus Platzgründen und aufgrund der Einfachheit können wir auf FetchValue hier nicht weiter eingehen. Interessanter ist die withOption-Zeile, die das Replikationsverfahren des Clusters in Bezug auf die vorliegende Anfrage beeinflusst. Im Hause Riak setzt man auf das Konzept der eventuellen Konsistenz: anders als in einer MySQL-Datenbank, die sofort nach dem Abarbeiten der Befehle konsistent ist, ist ein Riak-Cluster erst nach einiger Zeit – präzise gesagt, erst in der Unendlichkeit – komplett à jour.

StoreValue.Option.W legt fest, dass der Schreibbefehl erst dann erfolgreich abgeschlossen sein darf, wenn zumindest drei Teile des Clusters ihn bestätigt haben. In der Praxis bzw. in unserem System ist dies deshalb kein Problem, weil das API ausreichend Eigenintelligenz mitbringt, um festzustellen, dass der vorliegende Cluster zu einfach ist und entsprechend den vom Entwickler befohlenen Komplettierungswert niemals erreichen kann.

Daten in Bezug setzen

Informationen der realen Welt stehen nur sehr selten allein: Ob eine Kontaktdatenbank, ein Wertschöpfungsgraph oder beim Flussdiagramm, sind die angebotenen Daten im Allgemeinen miteinander verbunden. In der Welt der klassischen technischen Informatik bildet man derartige Systeme über die Graphentheorie ab: Ein mathematisch faszinierendes, bei Studenten ob seiner Komplexität allerdings verhasstes Verfahren. In der Welt der NoSQL-Datenbanken haben sich graphenorientierte Datenbanken mittlerweile einen fixen Platz erkämpft: Es gibt immer wieder Situationen, die man ohne graphenorientierte Datenbanken nicht vernünftig darstellen kann.

International PHP Conference 2018

Getting Started with PHPUnit

by Sebastian Bergmann (thePHP.cc)

Squash bugs with static analysis

by Dave Liddament (Lamp Bristol)

API Summit 2018

From Bad to Good – OpenID Connect/OAuth

mit Daniel Wagner (VERBUND) und Anton Kalcik (business.software.engineering)

Neo4J hat sich im Laufe der letzten Jahre als Quasistandard etabliert. Wir wollen dieses Produkt kurz anreißen, um die Möglichkeiten einer graphenorientierten Datenbank zu demonstrieren. Wichtig ist hierbei, dass das Produkt in zwei verschiedenen Versionen bereitsteht – neben einer als Service laufenden Variante gibt es auch die Möglichkeit, eine kleine Instanz von Neo4J direkt im Prozess der Hostapplikation laufen zu lassen. Die beiden Versionen unterscheiden sich in Sachen API komplett – während eine eingebundene Datenbank direkten Zugriff auf Objekte ermöglicht, bietet der alleinstehende Server eine eigene Abfragesyntax an.

Als Beispiel hierfür eine Transaktion, die eine Gruppe von Nodi in die Datenbank einpflegt. Im Interesse der Vollständigkeit sei an dieser Stelle angemerkt, dass die Nutzung des Transaktionsobjekts nicht komplett erforderlich ist:

try ( Transaction tx = session.beginTransaction() )
{
tx.run( "CREATE (n:SUSNodus { name: 'N1', title: 'Eins' })");
tx.run( "CREATE (n:SUSNodus { name: 'N2', title: 'Zwei' })");
tx.run( "CREATE (n:SUSNodus { name: 'N3', title: 'Drei' })");
tx.success();
}

Die Neo4J-Abfragesprache arbeitet mit Klassen: Der nach dem n: folgende String legt fest, zu welcher Gruppe der anzulegende Nodus gehört. Danach kommt eine Gruppe von JSON-Werten, die die eigentlichen Attribute beschreiben. Zum Anlegen einer Beziehung zwischen den Elementen ist eine etwas andere Syntax erforderlich:

StatementResult result = session.run("MATCH (a:SUSNodus),(b:SUSNodus)" +
"WHERE a.name = 'N1' AND b.name = 'N3'" +
"CREATE (a)-[r:HAELT]->(b)" +
"RETURN r");

Im ersten Schritt beschaffen wir hier unter Nutzung von MATCH einen Verweis auf zwei Objekte. Von besonderer Wichtigkeit ist das Übergeben des Namens der Klassen, um Neo4J das Einschränken der zu analysierenden Objekte zu ermöglichen. Mit der Where-Klausel wandern sodann Einschränkungen in die Struktur – sie sorgen dafür, dass nur bestimmte Objekte als A und B zurückgeliefert werden.

Das eigentliche Anlegen der Beziehung erfolgt dann mit einem weiteren Create-Befehl. Neo4J setzt hier auf eine eigenwillige Syntax, die sowohl Klammern als auch Pfeil und Co. voraussetzt.

Der Teil r:HAELT sorgt für die Festlegung des Beziehungstyps: Die Datenbank erlaubt Entwicklern das Anlegen verschiedenartiger Beziehungen zwischen den Nodes. Ein weiteres interessantes Beispiel präsentiert sich folgendermaßen:

MATCH (a:Person),(b:Person)
WHERE a.name = 'A' AND b.name = 'B'
CREATE (a)-[r:RELTYPE { name: a.name + '<->' + b.name }]->(b)
RETURN type(r), r.name

Hier wird eine Beziehung erstellt, die nicht nur einen Typ, sondern auch Attribute aufweist. Der besondere Reiz dieser Vorgehensweise besteht darin, dass Neo4J im Rahmen von Abfragen auch auf diese Metadaten eingeht. Wie im Fall der drei vorhergehenden Datenbanksysteme gilt allerdings auch hier, dass wir die Besprechung des Produkts an dieser Stelle beenden müssen. Unter https://neo4j.com/docs/developer-manual/current/cypher/ findet sich eine sehr umfangreiche Dokumentation des Herstellers, die die diversen Möglichkeiten von Neo4J im Detail vorstellt.

Weitere Datenbankschemata

In der Welt der NoSQL-Datenbanken tummelt sich eine Vielzahl weiterer Speichersysteme. Ein weit verbreitetes Schema ist die spaltenorientierte Datenbank: Es handelt sich hierbei um die Umkehrung einer gewöhnlichen relationalen Datenbank, die insbesondere dann ihre Leistungsfähigkeit ausspielen kann, wenn ein bestimmtes Attribut aller Objekte bearbeitet werden muss. Ein Beispiel dafür wäre die Berechnung der Lohnkosten eines Unternehmens. Hierzu muss das Lohnfeld jedes einzelnen Mitarbeiterobjekts berührt werden, während andere Attribute wie Dienstgrad, Geschlecht usw. nicht beachtet werden müssen. Zum Verständnis des Problems wollen wir die Informationen annehmen, die in Tabelle 1 stehen.

Name Dienstgrad Gehalt Rang
Schlomo A12 2 000 A
Omo A6 1 000 A
Dromo A8 1 200 B

Tabelle 1: Datenbanksystem mit einzelnen Mitarbeiterobjekten

Ein gewöhnliches relationales Datenbanksystem würde die einzelnen Attribute hintereinander serialisieren: Als Erstes liegen auf der Platte die Werte von Schlomo, dann die von Omo und dann die von Dromo. Die in Datacenters aufgrund der immer noch wesentlich günstigeren Pro-Gigabyte-Kosten eingesetzten Festplatten scheitern an dieser Stelle insofern, als sie zur Durchführung einer Analyse der Gesamtgehaltskosten jedes einzelne Gehaltsfeld anspringen müssen. Eine spaltenorientierte Datenbank würde Informationen stattdessen folgendermaßen speichern:

Schlomo//Omo//Dromo
A12//A6//A8
2000/1000//1200
. . .

Schon aus der Logik ist ersichtlich, dass die Manipulation einer spaltenorientierten Datenbank wesentlich mehr Aufwand verursacht, das Auswerten der Gehälter allerdings schneller passiert. Der Lesekopf muss nur den Beginn der Gehaltsspalte anfahren und die Werte einsammeln – eine Datenbank, die in manchen Fällen immens effizient sein kann.

Zu guter Letzt wollen wir hier noch kurz auf objektorientierte Datenbanken eingehen: Stellen Sie sich diese wie eine Art Hybride auf einer graphenorientierten und einer dokumentenorientierten Datenbank vor. Die Systeme spielen ihre Stärke naturgemäß im Bereich der OOP aus. Schon aus Platzgründen können wir an dieser Stelle auf sie allerdings nicht weiter eingehen. Das gilt auch für alle anderen Hybriden, die eine oder mehrere Datenbankprozeduren in sich vereinen.

Fazit

NoSQL-Datenbanken sind das digitale Äquivalent des Modulationsdomänenanalysators: Ein Produkt, das man nur vergleichsweise selten braucht. Braucht man es allerdings und weiß nicht, dass es existiert, so hat man ein Problem. NoSQL-Datenbanksysteme sind zudem ein faszinierendes Wissenschaftsfeld, mit dem sich die eine oder andere Stunde auf eine intellektuell anspruchsvolle Art und Weise totschlagen lässt. Schon in diesem Sinne hoffen wir, dass Sie mit diesem Artikel Freude haben und NoSQL einfach mal ausprobieren.

PHP Magazin

Entwickler MagazinDieser Artikel ist im PHP Magazin erschienen. Das PHP Magazin deckt ein breites Spektrum an Themen ab, die für die erfolgreiche Webentwicklung unerlässlich sind.

Natürlich können Sie das PHP Magazin über den entwickler.kiosk auch digital im Browser oder auf Ihren Android- und iOS-Devices lesen. In unserem Shop ist das Entwickler Magazin ferner im Abonnement oder als Einzelheft erhältlich.

Unsere Redaktion empfiehlt:

Relevante Beiträge

X
- Gib Deinen Standort ein -
- or -