2016-10-14 1 views
6

ich Neo4j für ein Projekt mit den offiziellen Neo4j Treiber für .NET hier bin mit:Neo4j über Bolzen-Protokoll hat eine sehr hohe Latenz

https://www.nuget.org/packages/Neo4j.Driver

Dieser Treiber funktioniert über das Bolzen Protokoll, meine Vermutung ist, dass ein spezielles Binärprotokoll effizienter wäre als die HTTP-API. Aber seit dem Start des Projekts habe ich relativ hohe Latenzen von Neo4j für selbst sehr einfache Operationen bemerkt. Wie wie folgt Mitnahmen ein Spiel 30-60ms wenn UserID ein indiziertes Feld ist und die Datenbank ansonsten völlig leer ist:

match(n:User { UserID: 1 }) return n.UserID 

Dieses Verhalten tritt sowohl auf meinem lokalen Rechner (in der Nähe von Null Netzwerk-Overhead) und die Produktionsumgebung . Ich habe heute damit begonnen, dies zu untersuchen, und festgestellt, dass die Abfrage schnell zurückkehrt, aber es dauert lange, bis tatsächlich die Ergebnisse angezeigt werden. Zum Beispiel dauert die folgende Abfrage 0.2ms bevor der Anruf auf localhost zurückgibt, aber dann ruft ToArray() auf result (Pufferung der Datensätze, die in diesem Fall ein einzelnes ganzzahliges Feld ist) erhöht die Zeit auf 60ms.

using (var driver = GraphDatabase.Driver($"bolt://localhost:7687", AuthTokens.Basic("neo4j", "1"))) 
{  
    using (var session = driver.Session()) 
    { 
     // 0.2ms to return from this call 
     var result = session.Run("match(n:User { ID: 1}) return n.ID"); 

     // Uncommenting this makes the whole thing take 60ms 
     // result.ToArray(); 
    } 
} 

Ich habe dann versucht die Gemeinde Neo4jClient Paket gefördert, die über HTTP funktioniert:

https://github.com/Readify/Neo4jClient

Mit der gleichen Abfrage wird die Gesamtzeit reduziert nur 0,5 ms:

var client = new GraphClient(new Uri("http://localhost:7474/db/data"), "neo4j", "1"); 
client.Connect(); 

client.Cypher.Match("(n:User { ID: 1})").Return<int>("n.ID").Results.ToArray(); 

Die Ausführung eines offizielleren Benchmarks ergibt die folgenden Ergebnisse, ein großer Unterschied zwischen dem boltbasierten offiziellen Treiber und dem HTTP-basierten Neo4jClient.

Host Process Environment Information: 
BenchmarkDotNet.Core=v0.9.9.0 
OS=Microsoft Windows NT 6.2.9200.0 
Processor=Intel(R) Core(TM) i7-4770 CPU 3.40GHz, ProcessorCount=8 
Frequency=3312642 ticks, Resolution=301.8739 ns, Timer=TSC 
CLR=MS.NET 4.0.30319.42000, Arch=32-bit RELEASE 
GC=Concurrent Workstation 
JitModules=clrjit-v4.6.1586.0 

Type=Neo4jBenchmarks Mode=Throughput Platform=X64 
Jit=RyuJit 

     Method |   Median |  StdDev | Scaled | Scaled-SD | 
------------- |--------------- |------------ |------- |---------- | 
    Neo4jClient | 382.5675 us | 3.3771 us | 1.00 |  0.00 | 
Neo4jSession | 61,299.9382 us | 690.1626 us | 160.02 |  2.24 | 

So ist der HTTP-Client ist 160x schneller wenn Netzwerk-Overhead ist zu vernachlässigen.

Ich lief auch den Benchmark auf unserer Produktionsumgebung und während der Unterschied nicht so groß war, war die HTTP-Methode immer noch 6x schneller (und meine Netzwerkverbindung zur Produktion ist ziemlich langsam).

Der vollständige Benchmark-Code:

public class Neo4jBenchmarks 
{ 
    private readonly IDriver _driver; 
    private readonly GraphClient _client; 

    public Neo4jBenchmarks() 
    { 
     _driver = GraphDatabase.Driver("bolt://localhost:7687", AuthTokens.Basic("neo4j", "1")); 
     _client = new GraphClient(new Uri("http://localhost:7474/db/data"), "neo4j", "1"); 
     _client.Connect(); 
    } 

    [Benchmark(Baseline = true)] 
    public void Neo4jClient() 
    { 
     _client.Cypher.Match("(n:User { ID: 1})").Return<int>("n.ID").Results.ToArray(); 
    } 

    [Benchmark] 
    public void Neo4jSession() 
    { 
     using (var session = _driver.Session()) 
     { 
     session.Run("match(n:User { ID: 1}) return n.ID").ToArray(); 
     } 
    } 
} 

Sowohl meine Maschine und Produktion läuft Neo4j CE 3.0.4 (derzeit die Community Edition), obwohl ich es auf Windows 10 renne und Produktion ist eine Linux-Maschine . Wir haben keine Einstellungen nach meinem Wissen optimiert, aber ich bezweifle, dass dies einen 160-fachen Leistungsunterschied erklären könnte.

Ich habe auch versucht, das Sitzungsobjekt wiederzuverwenden (was ich denke, ist eine sehr schlechte Idee, da es nicht threadsicher ist), da das Erstellen einer Sitzung eine Transaktion zu erstellen, um zu sehen, ob das einen Unterschied machte, aber es war nicht t auffällig.

Ich wünschte, ich könnte den Neo4jClient verwenden, aber wir müssen wirklich die Fähigkeit, beliebige String-Abfragen auszuführen, während der Neo4jClient stark auf eine fließende API angewiesen ist und während es einen Low-Level-String-Modus bietet, ist es veraltet und actively discouraged in the documentation.

+0

Ich bin völlig offen für die Möglichkeit, Strings für Abfragen zu verwenden, entweder einen Pull oder einen Fehler im Idealfall mit der Syntax, die Sie wollen, und wir können sehen, was wir tun können! –

+0

@ChrisSkardon - Das wäre großartig! Ich werde nachsehen, ob ich etwas hacken kann. Was ich im Grunde suche, ist ein Dapper für Neo4j: low level, Eingabe ist eine Zeichenkette und Parameter und ich bekomme einfach Hilfe beim Mappen des Ergebnisses auf ein Objekt. – JulianR

Antwort

4

Nach weiteren graben, verfolgte ich das Problem zum Neo4j.Treiberpaket speziell, da der Treiber für NodeJS nicht unter dem gleichen Problem litt.

Das Klonen der aktuellen source des Pakets, das Erstellen und Referenzieren der DLL direkt anstelle des NuGet-Pakets beseitigte das Problem vollständig. Zum Vergleich: Die aktuelle Version von NuGet (1.0.2) benötigt 62 Sekunden, um 1000 einfache Übereinstimmungsanfragen gegen localhost auszuführen, während die aktuelle Quelle dies in 0,3 Sekunden erledigt (sogar den NodeJS - Treiber durch einen Faktor von 10).

Ich bin nicht ganz sicher, warum, aber ich bin ziemlich sicher, dass es etwas mit der rda.SocketsForPCL Abhängigkeit des aktuellen Pakets zu tun hat, die eine Leim-Bibliothek zu sein scheint, um Sockets plattformübergreifend arbeiten zu lassen. Die aktuelle Quelle verweist jedoch auf das Paket System.Net.Sockets.

Also abschließend kann dieses Problem durch Bezugnahme auf einen aktuellen Build der Quelle umgehen und wird vollständig gelöst werden, wenn eine neue Version des Pakets veröffentlicht wird.

Verwandte Themen