Aus Linux-Magazin 06/2014

Netzwerk-Beziehungen in Neo4j speichern und auswerten

© Buchachon Petthanya, 123RF

Die Graphdatenbank Neo4j eignet sich viel besser als relationale Datenbanken, um Knoten und deren Beziehungen zueinander zu speichern und gezielt abzufragen. Wessen Freundeskreis nicht verworren genug ist, um als Graph-basierter Anwendungsfall durchzugehen, inventarisiert eben sein LAN damit.

Konstrukte wie der Social Graph von Facebook, der Verbindungen von Freunden und deren Bekanntschaften abbildet, oder die Follower-Struktur von Twitter sträuben sich hartnäckig, wenn es darum geht, ihre Daten persistent in traditionelle Datenbanken zu packen: Überführt man einen Pfad, der sich auf einem Whiteboard sehr einfach mit Kringeln und Pfeilen darstellen lässt, in ein relationales Modell, kommen Performance fressende Join-Anweisungen heraus – was sich mit den Anforderungen an eine schnell antwortende Website beißt.

Die Graphdatenbank Neo4j [2] dagegen speichert Graphmodelle nativ und legt dabei eine sagenhafte Performance hin – solange man es mit der Komplexität der Queries nicht übertreibt. Ihr generisches Speichermodell besteht aus Nodes (Knoten) mit Relationships (Kanten). Beide dürfen Attribute führen, so könnte ein Node, der eine Person repräsentiert, ein »name« -Feld zum Speichern des Namens enthalten oder eine Beziehung »is_friends_with« und deren Gütegrad (»best_friend« , »casual_friend« ).

Online PLUS

In einem Screencast demonstriert Michael Schilli das Beispiel: https://www.linux-magazin.de/news/der-perl-screencast-zum-linux-magazin-2014-06/

Abfragesprache Cypher

Die in der Datenbank liegenden Daten nimmt der Query-Prozessor von Neo4j in der SQL-ähnlichen Sprache Cypher entgegen und liefert reihenweise Ergebnisse, die Cypher ebenfalls im Stile von SQL filtert und aufbereitet (also sortiert, gruppiert et cetera).

Nach dem Installieren des GPL-lizenzierten Neo4j-Community-Servers (eine kostenpflichtige Enterprise-Version gibt’s auch) lauscht dieser auf Port 7474 auf Kommandos, die entweder über REST eingehen oder den neueren simplen Json-Prozessor nutzen. Programmieren lässt sich ein Client in mehreren Dutzend Sprachen, unter anderem mit dem CPAN-Modul REST::Neo4p.

Dem auf [2] angebotenen Debian-Paket liegt darüber hinaus eine praktische Kommandoshell bei: »neo4j-sh« . Mit ihr kann jedermann ähnlich wie mit dem interaktiven MySQL-Client Anweisungen absetzen, um neue Daten in das Modell einzufügen und gespeicherte Informationen über Cypher-Queries zu extrahieren.

Mächtig deklarativ

Cypher gibt sich, ähnlich wie SQL, deklarativ: Der User bestimmt, welche Ergebnisse er sucht, macht aber zugleich keine prozeduralen Angaben darüber, wie diese genau zu finden sind. Match-Anweisungen bestimmen, welche Daten interessieren (beispielsweise “Finde alle Daten” oder “Finde alle Relationen vom Typ »is_friends_with« ). Where-Klauseln reduzieren anschließend die Anzahl der Treffer, zum Beispiel könnten den anfragenden User nur Personen interessieren, die älter als 18 Jahre sind.

Am Ende modeln später einsetzende Aufbereitungsschritte die Daten um, sortieren oder fassen sie zusammen. Aber auch weitere Match-Anweisungen auf die Ergebnisliste und sogar zwischendurch einsetzende Aktionen zum Erzeugen neuer Daten sind erlaubt.

Zur Illustration einiger praktischer Abfragen soll der Graph eines Heimnetzwerks in Abbildung 1 dienen. Netze mit Knoten und Verbindungskanten stellen tatsächlich ein beliebtes Aufgabenfeld für Neo4j dar. Denn um festzustellen, ob ein Router über andere Knoten problemlos das offene Internet erreicht, muss die Datenbank einen offenen Pfad von A nach B über n oft trickreich verbundene Knoten finden, was auf relationalen Systemen eine Performance-Implosion verursacht, aber sich auf Graphdatenbanken oft in den Griff bekommen lässt.

Abbildung 1: Die Komponenten eines Home-Netzwerks – für die Analyse füttert sie der Perl-Snapshot in eine Graphdatenbank ein.

Abbildung 1: Die Komponenten eines Home-Netzwerks – für die Analyse füttert sie der Perl-Snapshot in eine Graphdatenbank ein.

Handgefüttert

Um nun zum Beispiel den Router mit dem Namen »internal« aus Abbildung 1 in die Datenbank einzufügen und ihm die LAN-IP 192.168.2.1 zuzuweisen, genügt in der Neo4j-Shell das Kommando:

neo4j-sh (?)$ CREATE (router {name:"internal", lan_ip:"192.168.2.1"});

Um die Relation »gateway« zwischen dem Router »internal« und seinem Gateway, einem weiteren neu erzeugten Knoten namens »merger« , anzulegen, sucht ein Cypher-Query beide Knoten wieder heraus und definiert mit der Cypher-eigenen Ascii-Art-Syntax die Verbindung:

neo4j-sh (?)$ MATCH (a), (b)
> WHERE a.name = "windows" and b.name ="merger"
> CREATE (a)-[r:gateway]->(b);

Die Match-Operation findet zwei Knoten, denen sie die Aliasnamen »a« und »b« zuweist. Da sonst kein gesuchtes Pattern in der Match-Klausel steht, trifft dies auf alle in der Datenbank liegenden Knoten zu. Die nachfolgende Where-Klausel schränkt die Treffer aber auf zwei exakt benannte Knoten ein, und die Create-Anweisung malt mit »-[…]->« einen Pfeil mit Namen zwischen die gefundenen Knoten und kreiert somit eine Relation vom Typ »gateway« .

Fütterungsautomat

Nun wäre es äußerst mühsam, die Daten eines großen Netzwerks von Hand einzutippen. Deswegen definiert die Datei »routers.yml« in Listing 1 die Eckdaten aller Router im lesbaren Yaml-Format. Die Verknüpfung der Router untereinander als Beziehung im Graphen ergibt sich später implizit aus der Verbindung der Gateway-Adresse des einen mit der LAN-IP des nächsten Routers.

Das Skript in Listing 2 schnappt sich die Yaml-Records aller Router aus der »routers.yml« -Datei, iteriert ab Zeile 28 über die Liste und pumpt sie dank des CPAN-Moduls REST::Neo4p als Nodes in die Datenbank. Dabei sichert sie alle Referenzen auf erzeugte Node-Objekte im Hash »%lans« unter deren LAN-IP als Schlüssel. Die Gateway-IPs hingegen speichert das Skript im Array »@gateways« samt den Routern, die diese Gateway-IPs nutzen. Ab Zeile 46 nudelt eine For-Schleife über »@gateways« , sucht über den »%lans« -Hash das Zielobjekt raus und definiert in der Datenbank mit der Methode »relate_to()« je eine »gateway« -Relation vom Start- zum Zielrouter.

DIESEN ARTIKEL ALS PDF KAUFEN
EXPRESS-KAUF ALS PDFUmfang: 5 HeftseitenPreis €0,99
(inkl. 19% MwSt.)
LINUX-MAGAZIN KAUFEN
EINZELNE AUSGABE Print-Ausgaben Digitale Ausgaben
ABONNEMENTS Print-Abos Digitales Abo
TABLET & SMARTPHONE APPS Readly Logo
E-Mail Benachrichtigung
Benachrichtige mich zu:
0 Comments
Inline Feedbacks
Alle Kommentare anzeigen
Nach oben