Teil I
Die fortschreitende Entwicklung in den Bereichen Big Data, Internet of Things, Machine Learning etc. und der Einsatz von entsprechenden Tools in Unternehmen sind nicht mehr wegzudiskutieren.
Wird unsere Welt in dem Bereich analytischer Systeme – so wie wir sie kennen – komplett auf den Kopf gestellt? Müssen wir zwangsläufig neue Technologien in diesen Bereichen einsetzen oder können die neuen Herausforderungen mit einem „Tuning“ bestehender Systeme bewältigt werden? Um diese Fragen aus Sicht eines Datenarchitekten zu beantworten, sollen die Unterschiede und Gemeinsamkeiten der ‚alten‘ relationalen und der ‚neuen‘ NoSQL–Datenbanken näher betrachtet werden.
Lässt man den Fokus auf den Big Data Bereich, so ist es nicht unbedingt nur die Datenmenge als solche, die den Unternehmen Mehrwert bringt, sondern vielmehr die genaue, umfassende und vor allem schnelle Analyse der Beziehungen innerhalb der Daten. In den letzten Jahren haben sich vier unterschiedliche Ansätze (Key-Value‑, spaltenorientierte, dokumentenorientierte und Graph- Datenbanken) in den Bereichen von NoSQL-DB herauskristallisiert. Allen voran sind es die Graph- Datenbanken, die es ermöglichen, große und dicht vernetzte Daten effizient zu analysieren. Im Folgenden wird der Unterschied zwischen Neo4J als einem der verbreitetsten Vertreter der Graph- Datenbankfamilie und relationalen DBMS im analytischen Bereich eingegangen.
Datenmodellierung
Das Herzstück jedes analytischen Systems ist das darunterliegende Datenmodell. Nur mit Hilfe eines domänengetreuen und konsistenten Modells lassen sich vertrauenswürdige Informationen und Prognosen gewinnen. Die Datenmodellierung ist nichts anderes als ein Prozess der Abstraktion, dabei werden erhobene Business-Anforderungen in tabellarische Speicherungsstrukturen übersetzt und die Daten in diese Strukturen bewirtschaftet. Klingt einfach, in der Realität handelt es sich um hochkomplexe Vorgänge innerhalb der relationalen Welt:
Zuerst die Erstellung eines logischen Datenmodells und dessen abschließende Überführung in ein physisches Datenmodell. Dabei kommt es mehrfach vor, dass sich das Endergebnis ‚physisches Datenmodell‘ von dem logischen Datenmodell wesentlich unterscheidet – erzwungen durch die spezifischen Anforderungen des jeweiligen RDBMS. Der Abstraktionslevel zwischen den Business Anforderungen und dem physischen Datenmodell kann dazu führen, dass die Abweichungen zwischen Business-Anforderungen und dem Produkt ‚analytisches System‘ erst sehr spät im Projektverlauf erkannt werden; z.B. während des User-Acceptance-Tests oder sogar erst in der Produktionsphase.
Hier lässt sich auch das wesentliche Manko der relationalen Datenbankmodelle erkennen – die mangelnde Flexibilität. Die relationalen DBMS sind ursprünglich entwickelt worden zur Verwaltung von Formularen und Tabellenstrukturen, also von klar strukturierten Daten. In diesem Bereich sind sie nach wie vor unschlagbar. Im Management von Beziehungen allerdings – insbesondere wenn diese ad hoc modifiziert, hinzugefügt oder entfernt werden sollen – sind die RDBMS weniger effizient.
Nachfolgend ein stark vereinfachter relationaler Datenmodellausschnitt zur Veranschaulichung:
Wenn ein potenzieller Versicherungsnehmer online nach neuen Versicherungsprodukten sucht, ist es oft der Fall, dass ihm weitere Versicherungsprodukte angeboten werden. Um dies zu ermöglichen, sollen beispielweise folgende Abfragen bearbeitet werden:
„Welche Kunden haben das Versicherungsprodukt XY genommen?“ oder „Welche Kunden, die das Versicherungsprodukt XY bereits besitzen, haben auch neue Verträge für das Versicherungsprodukt YZ abgeschlossen?“
Zur Beantwortung der Fragen müssen in einem relationalen Datenmodell mehrere Joins zwischen den Tabellen gemacht werden. Je komplexer und umfangreicher das Modell ist, um so zeitintensiver werden die Abfragen. Die schlechte Abfrageperformance erzwingt in solchen Fällen das zeit-und kostenintensive Re-Design des zugrundeliegenden Modells und die anschließende Datenmigration.
Graph-Datenbanken wie Neo4J stellen die Beziehung in das Zentrum des Modells. Die Erstellung eines Modells für Neo4J ist in seinen Anfängen analog zu dem oben beschriebenen für ein RDBMS. Die Ergebnisse der Business-Anforderungsanalysephase werden zuerst mit lo-fi Techniken wie z.B. Whiteboard Sketchers abgesteckt und eine Domänenübersicht geschaffen.
An dieser Stelle enden aber auch bereits die Gemeinsamkeiten.
Anstatt die Anforderungen starr in Tabellenform inkl. zugehörigen Primary-/Foreign Key-Regeln zu pressen, reichert man diese an:
Objekte werden zu Knoten, die Rollen zu Labels, Attribute zu Properties und Relationen zu Beziehungen. Näher betrachtet besteht ein graphorientiertes Datenmodell für eine Neo4J–Datenbank aus einer Reihe von Knoten und Kanten. Wobei Knoten die Objekte und Kanten die Beziehungen zwischen den Objekten abbilden. Bezogen auf unser obiges Beispiel könnte das graphorientierte Pendant zu dem RDBM-Modell wie folgt aussehen:
In der Praxis kommt es oft vor, dass ein Knoten unterschiedliche Rollen innerhalb des Graphs einnehmen kann – beispielsweise ist eine Person Versicherungsnehmer und Geschädigter zugleich. Es kommt somit der Bedarf auf, die Knoten gruppieren zu können. Dies ist mit Neo4J seit Version 2.0 mit Hilfe des Konstrukts ‚Label‘ für die Knoten möglich. Ein Knoten kann mehrere Labels besitzen, die jeweils seine spezifischen Rollen in dem betrachten Domänenbereich festlegen. Die Struktur des Graphmodells wird durch die Beziehungen (Kanten) zwischen den Knoten definiert. Eine Beziehungskante in Neo4J hat immer eine Richtung und einen Namen. Es gibt keine bidirektionalen Beziehungen, jede Beziehungskante hat einen Start- und einen Endknoten. Knoten und Beziehungskanten können mit Eigenschaften (Properties) versehen werden. Da die Properties das Modell mit zusätzlichen Metadaten für die Graph- Abfrage-Algorithmen erweitern, werden diese insbesondere für die Abfrageoptimierung herangezogen. Das Endergebnis ‚Property-Graph-Modell‘ bildet somit das logische Domänenmodell 1:1 in das physikalische, so dass hier die Abstraktionsproblematik gar nicht auftaucht, wie wir sie aus der relationalen Datenmodellierung kennen.
In den Projekten ist es daher üblich, zuerst eine User-Story zu erfassen und anschließend zusammen mit den Domänenexperten einen Entwurf des Domänenmodells (Graph Pattern) zu erstellen. Basierend auf dem erstellten Pattern werden einige wenige Knoten erstellt und miteinander verbunden. Danach erfolgt ein Test und die Sicherstellung, dass die in der User-Story beschriebenen Anwendungsfälle in dem Graph-Modell abgedeckt sind.
Datenabfragen
Steht das physikalische Modell inkl. Daten parat (die Bewirtschaftung der Daten mit Neo4J wird in einem separaten Beitrag behandelt), können die Daten abgefragt – oder wie man in der Graph –Welt sagt – traversiert werden. Traversierung bedeutet nichts anderes als das Verfolgen der Beziehungskanten (auch Pfade genannt), ausgehend von einem Startknoten ohne komplexe oder mehrfach geschachtelte Joins. Dieser eindeutige Vorteil einer Graph-Datenbank gegenüber einem RDBMS resultiert daraus, dass die Daten direkt miteinander verknüpft, gespeichert und abgefragt werden. Die Beziehungspfade eines Knotens werden in Neo4J in Form einer Liste direkter Verweise zu seinem Nachbarknoten abgespeichert.
Dies macht die Beziehungen in Neo4J zu den ‚first-class citizens‘. Die Beziehungspfade sind dabei nach Typ und Richtung strukturiert und können durch die Properties zusätzliche beschreibende Informationen enthalten. In den Fällen, wo eine relationale Datenbank eine Join-Operation erzwingt, nutzt Neo4J die Verweisliste und selektiert direkt die verbundenen Knoten. Jeder Knoten agiert somit wie ein ‚lokaler Index‘, auch indexfreie Adjazenz genannt. Dank dieser Speicherungsmethodik eignet sich Neo4J insbesondere für die Verwaltung stark vernetzter Daten, z.B. für Routenberechnungen, Online-Einkäufe und Recommendations.
Ein Vorteil von Neo4J gegenüber anderen Graph-Datenbanken ist die Abfragesprache Cypher. Die Abfragepatterns in Cypher sind denen eines relationalen SQL sehr ähnlich, so dass hier die Lernkurve für die Entwickler mit SQL-Kenntnissen steil ist.
Um die Frage zu beantworten „Welche Kunden haben die Verträge mit dem Produkt „Auto“ abgeschlossen?“ müsste man in einer relationalen DB-Welt mehrere Tabellen miteinander joinen. Bezogen auf Abbildung 1 sähe die Abfrage wie folgt aus:
select knd.kunden_name, vrg.vertrag_id
from kunde knd
inner join kunde_vertrag_rel pvx on knd.kunden_id = pvx.kunden_id
inner join vertrag vrg on vrg.vertrag_id = pvx.vertrag_id
inner join vertrag_versicherungsprodukt_rel vxprodx on vrg.vertrag_id=vrprodx.versicherungsprodukt_id
inner join versicherungsprodukt vprd on vprd.versicherungsprodukt_id = vprodx.versicherungsprodukt_id
where vprd.bezeichnung = 'Auto'
Abfragen dieser Art in großer Menge und im Sekundentakt gestellt (wie in der Online-Geschäftswelt üblich), bringen relationale DB an ihre Grenzen. Eine Graph-Datenbank wie Neo4J ist in vergleichbaren Anwendungsfällen mit großen Datenmengen dank der indexfreien Adjazenz deutlich schneller.
Hier die gleiche Abfrage in Cypher:
match(v:Person)-[:hat]-(Vertrag)-[:deckt]-(VrtgGegenstand) where VrtgGegenstand.Bezeichnung = "Auto"
return v, Vertrag.
Das Ergebnis wird direkt visualisiert:
Befüllt man Neo4J mit den Knoten und Beziehungen gemäß der Pattern (Abbildung 2), so sieht das Ergebnis aus wie in Abbildung 3. Die direkte Visualisierung der Daten in Neo4J ist ein wichtiges Vehikel, welches es ermöglicht, die Beziehungen zwischen den einzelnen Objekten einfach und schnell zu erkennen und entlang der Pfade zu navigieren (Abbildung 5). Ändern sich die Beziehungen oder müssen die Objekte anders als bisher verknüpft werden, so können in dem bestehenden Graphen die neuen Beziehungen zwischen den Objekten erzeugt werden ohne die alten vorher löschen zu müssen. So kann man die leidigen Migrationsaufgaben umgehen. Es wird nur entlang der interessierenden Pfade traversiert, so dass es ermöglicht wird, unterschiedliche Hierarchien gleichzeitig im System zu halten und abzufragen, ohne den Graphen neu aufzusetzen.
Auf diesen können unterschiedliche Abfragen über Cypher realisiert werden:
Welche Verträge hat Johan Müller abgeschlossen? Zur Beantwortung die Abfrage in Cypher:
$match(prs:Person)-[:hat]-(vertrag) where prs.name = "Johan Müller" return prs, vertrag.
Die graphische Oberfläche ermöglicht es durch das Navigieren innerhalb des Graphen Details zu den einzelnen Nodes zu holen oder zu löschen ohne den Code extra erweitern zu müssen.
Fazit
Stehen bei einem Projekt die Analyse von Daten-Beziehungen im Fokus, so sind eindeutig die graphorientierten Datenbanken die allererste Wahl – insbesondere Neo4J mit ihrer für diese Zwecke optimierten Speicherungsstruktur.
Beispiele für den Einsatz einer graphorientierter Datenbank sind:
- Das Aufspüren von Verknüpfungen zwischen einzelnen Objekten (Betrugsfälle in der Versicherungs- oder Finanzbranche)
- Routenplanung und Tracking in global agierenden Logistikunternehmen
- Proaktives Online-Management.
In den Anwendungsfällen, bei denen der Schwerpunkt auf der Aggregation hoch strukturierter Daten liegt, sind nach wie vor die relationalen Datenbanken unschlagbar.
Zusammenfassend ist zu konstatieren, dass die graphorientierten Datenbanken eine Erweiterung darstellen – aber (noch) nicht die bestehenden relationalen DBMS komplett ersetzen können.