Seit Jahrzehnten haben sich relationale Datenbanken in allen geschäftsrelevanten Unternehmensanwendungen bewährt. Sowohl kommerzielle als auch Open-Source Datenbanken wie beispielsweise PostgreSQL erfüllen in 999 von 1000 Fällen alle Anforderungen. Doch spätestens seit dem NoSQL-Hype gibt es keinen Automatismus bei der Persistenzwahl mehr. Es wird nachgedacht, welche Persistenztechnologie am Besten passt.
Dieser Artikel ist kein Vergleich mit relationalen Datenbanken oder anderen Persistenztechnologien, die man unter NoSQL zusammenfasst, soll aber Appetit auf Graphendatenbanken machen. Ich werde in diesem Artikel anders vorgehen als andere Einleitungen zum Thema und beginne nicht mit den mathematischen Grundlagen von Graphen und werde weitere Aspekte von Graphendatenbanken erst im Laufe des Artikels einführen, sobald es didaktisch sinnvoll ist. Wer also schon Kenntnisse hat, wird beim Lesen eventuell unruhig auf dem Stuhl wackeln.
Doch warum sollte man alles erlernte über den Haufen werfen und sich dem Wagnis aussetzen (und diesen Artikel lesen)?
Wissen ist Macht
Relationale Datenbanken bestehen stark vereinfacht aus zweidimensionalen Tabellen und Relationen. Diese Form der Speicherung eignet sich bestens für tabellarische Informationen. Jedoch sind die Relationen stumpfe Verknüpfungen ohne jede Aussagekraft. Die Informationen sind zwar in der Datenbanken vorhanden, aber nur durch die in der Anwendung implementierten Geschäftsprozesse bekommen sie eine Bedeutung. Bei Graphendatenbanken ist dies anders. In einer Graphendatenbank ist es möglich Informationen semantisch abzulegen, d.h. sowohl die Informationen als auch ihre Bedeutung werden in der Graphendatenbank gespeichert.
Im Wesentlichen besitzt man ein Schema, das allen Knoten und Kanten typisiert. Beispielsweise ist ein möglicher Knotentyp “Person” vorhanden. Durch Vererbung kann man den Knotentyp “Person” spezialisieren, z.B. “Autor”. Ein Autor hat dieselben Eigenschaften wie eine Person und hat darüber hinaus zusätzliche Eigenschaften. Zu einem Graphen wird es, indem zwischen den konkreten Knoten Verknüpfungen gebildet werden. Auch dies wird im Schema modelliert. Beispielsweise besteht zwischen dem Knotentyp “Person” und dem Knotentyp “Artikel” die Beziehung, d.h. den Kantentyp “hat gelesen”. Auch Attribute für Beziehungen sind in einem Property-Graphen möglich. So hat die Beziehung “hat gelesen” das Attribut “gelesen am”. Somit würde man in einem Content-Management-System auf einfachste Weise abbilden können, ob und wann ein Benutzer einen Artikel gelesen hat indem einfach zwischen beiden Knoten eine Verknüpfung erstellt wird. Ein weiteres Beispiel wäre ein Kantentyp “hat geschrieben” zwischen den Knotentypen “Autor” und “Artikel”. Da es sich um einen gerichteten Graphen handelt, können wir auch die Richtung der Beziehung bestimmen, d.h. vom Autor zum Artikel und nicht umgekehrt. Zusätzlich stellen wir fest, dass die Vererbungshierarchie greift: Da ein Autor gleichzeitig eine Person ist, bestehen zwischen einem Knoten vom Typ “Autor” und vom Typ “Artikel” zwei mögliche Beziehungen. Als Autor hat er möglicherweise den Artikel geschrieben, als Person hat er möglicherweise den Artikel gelesen. Das besondere daran ist, dass wir nicht erst den Quellcode einer Anwendung lesen müssen, um den Sinn der Daten zu begreifen, wie es mit einem Heer von Tabellen, M:N-Tabellen, Primär- und Fremdschlüsseln der Fall wäre.
Doch das ist nicht nur schön sondern auch nützlich: Dadurch, dass wir wissen, was unsere Daten bedeuten, können wir in unseren Anwendungen entsprechende Abfragen stellen. Uns interessiert beispielsweise die Frage, von wievielen Autoren ein Artikel geschrieben wurde. Nichts einfacher als das: man zählt alle Verknüpfungen vom Kantentyp “hat geschrieben” die zum Knoten des besagten Artikel führen. Noch interessanter erscheint jedoch die Frage, welche Autoren für einen bestimmte Person interessant sind. Spätestens an dieser Stelle geraten relationale Datenbanken ins Hintertreffen. Mit einer Graphendatenbank müssen wir jedoch nur den Graphen betrachten: Die betreffende Person hat mehrere Artikel gelesen und diese Artikel wurden von Autoren geschrieben. Wir “navigieren” nun durch den Graphen und “zählen”. Derjenige Autor ist am meisten interessant, der die meisten Artikel geschrieben hat, die die betreffende Person bereits gelesen hat. Andere Autoren haben vielleicht mehr Artikel geschrieben, aber diese wurden von der betreffenden Person nicht gelesen, und daher sind diese Autoren nicht so interessant. Nun fehlt noch die Frage, welche Artikel man empfehlen könnte. Wir wissen, welche Artikel eine Person gelesen hat. Diese scheiden also aus. Des weiteren wissen wir, welche Autoren interessant sind. Diese beiden Erkenntnisse verknüpfen wir nun, und erhalten ein Ergebnis: Alle von der betroffenen Person noch nicht gelesenen Artikel des interessantesten Autors. Durch Erweiterung des Schemas könnte man das Ergebnis weiter verfeinern. Beispielsweise können Personen Artikel “liken” oder bewerten, ein Artikel kann ein Thema “behandeln” und eine Person kann an einem Thema “interessiert” sein.
Trotz dessen, dass wir ein Schema haben, das nur aus drei Knotentypen und zwei Kantentypen besteht, können wir bereits einige Schlüsse ziehen. Und genau dies ist der Grund, warum Graphendatenbanken anderen Persistenztechnologien überlegen sind. Das Schema wird niemals so kompliziert werden, wie ein Schema in einer relationalen Datenbank, in der beispielsweise Tabellen nur existieren, um M:N-Relationen abzubilden. Das Schema und mögliche Fragestellungen lassen sich wunderbar auf Papier skizzieren. Auch auch der Graph selbst lässt sich visualisieren.
Visualisierung
Ein weiterer Vorteil eines Graphen ist es, in visualisieren zu können. Üblicherweise zeichnet man Knoten als Kreise oder Kästchen, die durch Linien oder Kurven verbunden sind, die die Kanten darstellen. Durch das Betrachten des Graphen kann man intuitiv erkennen, welche Knoten, bezogen auf einen bestimmten Aspekt, wichtiger sind als andere. Würden wir unser Beispiel von oben visualisieren, würden wir sofort erkennen, welche Autoren viele Artikel geschrieben haben, welche Personen viele Artikel gelesen haben. Durch entsprechende Zeichenalgorithmen ist es sogar möglich, dass mit Kanten verbundene Knoten wie Gewichte aneinander ziehen. Das Ergebnis ist, dass zwei Knoten, die viel miteinander zu tun haben, näher beieinander sind, als Knoten, die weniger miteinander zu tun haben. Da mit wachsendem Datenbestand die Anzahl von Knoten und Kanten unüberschaubar werden kann, wird man sicherlich eine Auswahl treffen, welche Knotentypen und Kantentypen für eine bestimmte Fragestellung überhaupt relevant sind.
Die Navigation im Graphen
Der Unterschied zu Big-Table, Key-Value-Stores und relationale Datenbanken ist, dass man innerhalb eines Graphen navigieren kann. Dadurch lassen sich einfache und sogar komplexere Fragestellungen beantworten, wie oben gezeigt. Noch nicht angesprochen ist die Tatsache, dass das Navigieren im Graphen weniger Zeit beansprucht als die Abfrage von Tabellendaten, wie es beispielsweise mittels SQL gemacht wird. Doch dazu muss man sein “Denken” vom Modus “ich denke in Tabellen” auf “ich denke in Graphen” umstellen. Das bedeutet, dass man auch das Schema anders erstellt, als man es bei relationalen Datenbanken tun würde. Wie bei relationalen Datenbanken könnte man einem Artikel ein Attribut für das Erstellungsdatum zuweisen. Will man nun alle Artikel eines Jahres haben, müsste man in allen Knoten, die vom Knotentyp “Artikel” sind, das Datum überprüfen. Da man dabei jedoch alle Knoten betrachten muss, wird viel Zeit benötigt. Zwar ist es möglich, die Abfragezeiten mittels eines Index deutlich zu senken, trotzdem ist es nicht die effektivste Methode, um die gewünschten Artikel zu erhalten. Stattdessen liegt die Lösung im Schema: Die Knotentypen “Jahr”, “Monat” und “Tag” erlauben es einen Knoten “Jahr 2013” für das Jahr 2013 oder einen Knoten “05. Dezember 2013” für den heutigen Tag zu erstellen. Diese Knoten werden durch die Kantentypen “hat Monat” und “hat Tag” verknüpft. Zusätzlich wird der Kantentyp “wurde erstellt am” genutzt um auszusagen, dass ein Artikel an einem bestimmten Tag erstellt wurde. Um nun alle Artikel aus dem Jahr 2013 zu finden, muss man beim Jahresknoten 2013 beginnen, alle Monate und deren Tage um schließlich alle Artikel einzusammeln. Das Verfahren lässt sich ohne Änderung auch auf Monate oder Tage anwenden. Vorteil dieses Verfahrens ist, dass es schnell ist. Die Navigation von einem Knoten zu einem anderen Knoten liegt im Nanosekundenbereich. Zusätzlich erspart man sich Vergleichsoperatoren und Indexlookups. Stattdessen werden nur Knoten “eingesammelt”. Voraussetzung ist lediglich, dass man von einem Knoten aus starten kann, in unserem Beispiel war das der Knoten “Jahr 2013”.
Fazit
Graphen sind nützlich, wenn die Informationen miteinander vernetzt sind. Graphendatenbanken liefern die Umgebung, die nötig ist, um Graphen performant zu nutzen. In diesem Artikel sollte vermittelt werden, wann sich der Einsatz von Graphendatenbanken lohnt. Im nächsten Teil werde ich euch mit der Praxis konfrontieren: Das in diesem Artikel genutzte Beispiel wird mit Neo4j, Spring und Spring Data Neo4j umgesetzt.