Mehr Power für Graphen

Die Highlights der Graphdatenbank Neo4j 3.0

Michael Hunger

In den vergangenen fünfzehn Jahren hat sich für Neo4j viel getan und die einstige Java-Bibliothek zur Verwaltung von Datennetzen ist jetzt eine weltweit genutzte Datenbank. Die Version 3.0 hat in drei Kernbereichen einen Sprung nach vorne gemacht: Leistung, Anwenderfreundlichkeit und Infrastruktur.

Knapp sechs Monate nach dem vorherigen Release 2.3 war der Versionssprung auf 3.0 gerechtfertigt, denn die 3er-Version von Neo4j bringt eine große Menge neuer Features, Verbesserungen und Änderungen mit. Vor allem bei der Skalierbarkeit und der Leistung hat sich einiges getan. Denn obwohl Graphdaten nicht als Massendaten ausgelegt sind, und unsere Anwender die bislang existierenden Limits nie erreicht haben, gab es schon seit Längerem den Wunsch, diese zu entfernen. Eigentlich möchte man in seinem Graphmodell nur qualitativ hochwertige Daten speichern, bei denen durch Vorverarbeitung schon Rauschen und Artefakte entfernt wurden – es sei denn, man ist auf der Suche nach solchen Artefakten. Durch den immer breiteren Einsatz besonders im Handel, Telekommunikations- und Netzwerkumfeld, sowie bei Industrie 4.0 und dem Internet of Things wird die Menge der gespeicherten Graphdaten in den nächsten Jahren jedoch beträchtlich ansteigen, sodass wir schon jetzt dafür vorsorgen wollten.

Daher ist jetzt die Anzahl der Knoten, Kanten und Eigenschaften in einem Neo4j-Graph nicht mehr künstlich limitiert. Die Größe der Dateisysteme und die Schreibgeschwindigkeit von Festplatten sind damit die begrenzenden Faktoren. Um trotz der größeren Adressierbarkeit den Platzbedarf für die größeren Block-Offsets zu verringern, nutzen wir komprimierte Pointer, die nur minimalen Speicher benötigen. Für unsere Lucene-Integration haben wir ein automatisches Sharding implementiert, da ein einzelner Lucene-Index nur 2,4 Milliarden Dokumente umfassen kann.

Außerdem wurde in vielen Integrations- und Clustertests sichergestellt, dass Neo4j auch auf großen Maschinen gut skaliert. Innerhalb unserer Partnerschaft mit IBM wird die Datenbank auf Maschinen mit bis zu 160 Kernen und bis zu 512 GB RAM auf Herz und Nieren Stresstests unterzogen. Für die Zukunft ist die Unterstützung von CAPI-Flash mit mehr als 50 TB RAM in Arbeit.

Unser effizienter, kostenbasierter Abfrageplaner für Cypher wird jetzt auch für Statements genutzt, die in die Datenbank schreiben. Bisher war das nur für reine Leseabfragen der Fall. Besonders bei komplexen, gemischten Lese-Schreib-Abfragen profitiert man jetzt vom höheren Durchsatz, da durch die transaktional aktuellen Statistiken bessere Entscheidungen für die Ermittlung eines Ausführungsplans gemacht werden.

Blockchain Whitepaper 2018

Free: Blockchain Technology Whitepaper

If building a blockchain from scratch is beyond your current scope, the blockchain technology whitepaper is worth a look. Experts from the field share their know-how, tips and tricks, development advice, and strategy for becoming a blockchain master.

APIs einfacher einsetzen

Bisher konnte der Neo4j-Server über HTTP-APIs angesprochen werden. Diese wurden sowohl von Datenbanktreibern als auch Webanwendungen wie unserer eigenen Oberfläche, dem Neo4j-Browser, direkt genutzt. Für die Version 3.0 wurde ein neues Binärprotokoll namens Bolt entwickelt. Dieses lässt neben der kompakten und schnellen Datenübertragung auch Raum für zukünftige Entwicklungen. Es ist ein versioniertes Protokoll. Ein Server kann deswegen mehrere Clientversionen unterstützen. Für Sicherheit ist mit zertifikatsbasierter Verschlüsselung der Transportschicht (TLS) und Authentifizierung gesorgt. Der Transport erfolgt über TCP Sockets und über WebSockets im Browser. Das Protokoll basiert auf einer Variante von MessagePack namens PackStream, die zusätzlich komplex geschachtelte Datentypen, Graphelemente wie Knoten, Beziehungen und Pfade sowie generelle Erweiterbarkeit bereitstellt.

Für die Nutzung des neuen Protokolls gibt es erstmals in der Geschichte von Neo4j offiziell unterstützte Treiber [1]. Bisher haben wir, wie für Open-Source-Projekte häufig der Fall, Treiber genutzt, die von Entwicklern in unserer Nutzergemeinschaft entwickelt wurden. Jetzt wollen wir unseren Nutzern zumindest einen konsistenten Basistreiber für die meisten Programmiersprachen anbieten. Für Java, JavaScript, .NET und Python ist das jetzt schon der Fall. Die C- und PHP-Treiber für das neue Protokoll wurden in unserer Partnercommunity entwickelt. Bolt-Treiber für weitere Sprachen werden in Zukunft folgen.

Da alle Treiber dieselben Konzepte und APIs implementieren und das Cypher-Typsystem zur Anwendung bringen, sieht das API in fast jeder Sprache ähnlich aus. Wer Java kennt, wird Parallelen zu anderen Datenbank-APIs wie JDBC erkennen. Hier am Beispiel von Java:


Driver driver = GraphDatabase.driver("bolt://localhost");
try (Session session = driver.session()) {
  StatementResult result = session.run(
    "MATCH (p:Person) WHERE p.name CONTAINS {name} RETURN p",
                            Values.parameters("name","Sven"));
  while (result.hasNext()) {
    Record record = result.next();
    record.get("name");
    record.get("age");
  }
}

Diese neuen Treiber lassen sich leicht in eigene Anwendungen einbinden. Sie sind leichtgewichtig und stehen unter der liberalen Apache-2-Lizenz. Zwei Projekte, die schon Gebrauch davon machen, sind der Neo4j-JDBC-Treiber [2] und der neue Apache Spark Connector [3]. Demos wie die Integration in Webanwendungen und Beispielanwendungen sind verfügbar [4].

Neo4j und JDBC

Zuerst einmal fragt man sich natürlich, wie denn eine Graphdatenbank, die gar kein SQL spricht und keine Tabellen kennt, mit JDBC klarkommt. Im Kern ist JDBC da flexibel, es werden textuelle, parametrisierbare Abfragen (Strings) an einen Treiber geschickt und tabellarische Ergebnisse zurückerwartet. Wie der Abfragetext aussieht (in unserem Fall Cypher) oder welches Datenmodell die Datenbank fährt (in Neo4j Graph) ist JDBC herzlich egal. Wenn die Datenbank dazu noch Transaktionen unterstützt, kann man ein großes Featureset von JDBC direkt nutzen. Das zu implementieren war gar nicht schwer. Wir hatten bisher schon einen JDBC-Treiber, der auf den HTTP-APIs aufgesetzt hat. Jetzt konnten wir eine sauberen Neuimplementierung basierend auf dem offiziellen Java-Bolt-Treiber vornehmen. Mittlerweile wurden auch HTTP und Java-Embedded als Protokolle nachgerüstet. Hier ein Beispiel für die Benutzung:

Connection con = DriverManager.getConnection("jdbc:neo4j:bolt://localhost");

String query = "MATCH (p:Person)-[:LIKES]->(b:Book) WHERE p.age < {1} "+
               "RETURN b.title as title, count(*) as freq";
try (PreparedStatement pstmt = con.prepareStatement(query)) {
  pstmt.setInt(1,25);
  ResultSet rs = pstmt.executeQuery();
  while (rs.next()) {
    System.out.println(rs.getString("title")+" "+rs.getInt("freq"));
  }
}
con.close();


 

Mit der Verfügbarkeit eines JDBC-Treibers stehen einem plötzlich alle Tools, Integrationsbibliotheken und -frameworks zu Verfügung. Besonders für Nichtentwickler ist die Nutzung von Daten in Neo4j mittels Reporting- (JasperReports, BIRT), BI- (QlikView, Tableau, Cognos), ETL- (Talend, Pentaho) und Analysetools (MathLab) möglich. Für Entwickler stehen alle Frameworks offen, die eine JDBC-Integration haben, z. B. Spring, MyBatis oder Play. Das gilt auch für Datenbanktools wie SQuirreL SQL, SQL-Explorer, IntelliJ oder DataGrip.

Einfache Verbindung zu Spark

Apache Spark ist als speicherbasierte, massiv-parallele Datenverarbeitungslösung in aller Munde, und die meisten IT- und BI-Abteilungen machen ihre ersten oder zweiten Schritte damit. Für viele Datenquellen und -senken bietet Spark selbst schon Integrationen an, z. B. auch über JDBC. Andere sind über das spark-packages.org-Repository verfügbar. Für Neo4j ist besonders interessant, dass Spark auch massiv-parallele Graphoperationen unterstützt, sodass man z. B. eine Milliarde Beziehungen für einen Page-Rank-Algorithmus verarbeiten kann. Daher war mit der Verfügbarkeit des Bolt-Treibers die Möglichkeit gegeben, einen offiziellen Neo4j-Spark-Connector zu entwickeln. Dieser kann, basierend auf Cypher, parallele Lese- und Schreiboperationen auch in transaktionalen Batches ausführen und so den effizienten bidirektionalen Datentransfer sicherstellen. Nach Einarbeitung in die Konzepte von Spark war es mir möglich, Unterstützung für RDDs (Resilient Distributed Dataset), DataFrames, GraphX und GraphFrames zu implementieren. Listing 1 und 2 zeigen als Beispiel ein soziales Netzwerk mit der Spark-Shell und GraphX für das Erzeugen eines sozialen Netzwerks mit 100 000 Personen und einer Million Beziehungen.


CREATE CONSTRAINT ON (p:Person) ASSERT p.id IS UNIQUE;

UNWIND range(1,100000) AS x
CREATE (n:Person {id:x, name:"name"+x, age:x%100})
WITH n
UNWIND range(1,10) as round
MATCH (m:Person) WHERE m.id = toInt(rand()*100000)
CREATE (n)-[:KNOWS]->(m);

$SPARK_HOME/bin/spark-shell --packages neo4j-contrib:neo4j-spark-connector:1.0.0-RC1

 


import org.neo4j.spark._

val g = Neo4jGraph.loadGraph(sc, label1="Person", relTypes=Seq("KNOWS"), label2="Person")
// g: org.apache.spark.graphx.Graph[Any,Int] = org.apache.spark.graphx.impl.GraphImpl@57498

// Größe des Teilgraphen
g.vertices.count          // res0: Long = 999937
g.edges.count             // res1: Long = 999906

// PageRank mit fünf Iterationen
import org.apache.spark.graphx._
import org.apache.spark.graphx.lib._

val g2 = PageRank.run(g, numIter = 5)

// Rang von fünf Knoten
val v = g2.vertices.take(5)
// v: Array[(org.apache.spark.graphx.VertexId, Double)] =
//    Array((185012,0.15), (612052,1.0153), (354796,0.15), (182316,0.15), (199516,0.385))

// die Ranginformationen werden als 'rank' Attribut nach Neo4j zurückgeschrieben
Neo4jGraph.saveGraph(sc, g2, nodeProp = "rank", relProp = null)
// res2: (Long, Long) = (999937,0)

 

Mit benutzerdefinierten Prozeduren arbeiten

Wie auch in anderen Datenbanken wird es mit Neo4j 3.0 möglich, spezifische Erweiterungen der Abfragesprache als Prozeduren einzubringen. Die Prozeduren lassen sich in Java und anderen JVM-Sprachen mittels annotierter Methoden implementieren.

Hier ein einfaches Beispiel zur Generierung von UUIDs: Die Prozedur ruft die UUID-Klasse aus dem JDK auf, um die UUID zu generieren, und verpackt sie dann in ein einfaches DTO, das für die Spaltenzuordnungen der Prozedurergebnisse verantwortlich ist. Danach werden die Ergebnisse, je nach Kardinalität der Prozedur, mit einem Java-8-Stream an Neo4j zurückgegeben und von Cypher an den Aufrufer weitergereicht. Da Cypher eine Sprache ist, die ihre Ergebnisse inkrementell bereitstellt, kann dieser Stream in geeigneten Fällen direkt bis zum Endnutzer durchgereicht werden:

@Procedure("apoc.create.uuid")
public Stream<UUIDResult> uuid() {
  return Stream.of(new UUIDResult(UUID.randomUUID().toString()));
}
static class UUIDResult {
  public final String uuid;
  ...
}

 

In Cypher werden Prozeduren mittels CALL procedure(parameter) YIELD result integriert. Es gibt auch einen Standalone-Modus, in dem der Prozeduraufruf das ganze Statement ausmacht:

LOAD CSV WITH HEADERS FROM {url} AS row
CALL apoc.create.uuid() yield uuid
CREATE (:Person {id:uuid, name: row.name, born: row.dob });


Natürlich ist in einer solchen Prozedur alles möglich, was das Java-Backend zu bieten hat. So hat man viele Möglichkeiten, aber auch die Verantwortung dafür. Nützliche Prozeduren sind: Komplexe Graph-Algorithmen, das zur Verfügungstellen von Neo4j-Java-APIs in Cypher, Datenkonvertierung, Import und Export. Aber auch ausgefallenere Sachen, wie Datentransfer von und zu anderen Datenbanken und APIs sind möglich, genauso wie die Generierung von Metainformationen. All das haben wir in unserem APOC-Projekt (Awesome Procedures for Neo4j) [5] umgesetzt, in dem jetzt mittlerweile knapp 200 Prozeduren viele verschiedene Anwendungsbereiche abdecken.

Generalüberholte Laufzeitinfrastruktur

Die Laufzeitinfrastruktur von Neo4j ist über die Jahre gewachsen. Dadurch sind sich einige Inkonsistenzen entstanden. Mit der aktuellen Version wurde dieser Teil der Datenbank generalüberholt. Die Startskripte, Konfigurationsdateien, Logdateien, Betriebssystemintegration und Installationspakte wurden überarbeitet und konsolidiert. Damit sollte auch die Integration in Unix-Systemlandschaften leichter werden.

Wer bisher Neo4j genutzt hat, sollte sich auf jeden Fall die Upgradedokumentation [6] anschauen, da sich in diesem Bereich fast alle Namen, Verzeichnisse und Dateien geändert haben. Für die leichteren Wechsel auf die neue Version gibt es ein Konfigurationsmigrationstool und den direkten Import einer Datenbank und Konfiguration:

java -jar $NEO4J_HOME/bin/tools/config-migrator.jar pfad/zu/neo4j2.x pfad/zu/neo4j3.x

$NEO4J_HOME/bin/neo4j-admin import --mode=database --database=graph.db --from=pfad/zu/neo4j2.x


 

In Neo4j 2.3 wurde die umfangreiche PowerShell-Integration für Windows vorgenommen. Diese wurde jetzt in einfachere Skripte gekapselt. Die Docker-Images und Debian-Pakete wurden ebenso aktualisiert.

Das aktuellste Release ist unter [7] zu finden, mit Installern für Windows und OS X und einem Binärdownload für alle anderen Betriebssysteme. Es ist über Docker (docker run neo4j), auch im neuen Docker Store, und als Debian- und Homebrew-Pakete verfügbar.

Verwandte Themen:

Geschrieben von
Michael Hunger
Michael Hunger
Software zu entwickeln, gehört zu Michael Hungers großen Leidenschaften. Der Umgang mit den beteiligten Menschen ist ein besonders wichtiger Aspekt. Zu seinen Interessen gehören außerdem Software Craftsmanship, Programmiersprachen, Domain Specific Languages und Clean Code. Seit Mitte 2010 arbeitet er eng mit Neo Technology zusammen, um deren Graphdatenbank Neo4j noch leichter für Entwickler zugänglich zu machen. Hauptfokus sind dort Integration in Spring (Spring Data Graph Project) und Hosting-Lösungen. Zurzeit hilft er der Neo4j-Community dabei, mit der Graphdatenbank ihre Wünsche wahr werden zu lassen. Michael arbeitet(e) an mehreren Open-Source-Projekten mit, ist Autor, Editor, Buchreviewer und Sprecher bei Konferenzen. Neben seiner Familie betreibt er noch ein Buch- und Kulturcafé (die-buchbar.de) in Dresden, ist Vereinsvorstand des letzten großen deutschen MUDs (mg.mud.de) und hat viel Freude an kreativen Projekten aller Art.
Kommentare

Hinterlasse einen Kommentar

Hinterlasse den ersten Kommentar!

avatar
400
  Subscribe  
Benachrichtige mich zu: