Neo4j over bolt协议具有非常高的延迟

时间:2016-10-14 19:30:56

标签: .net performance neo4j latency neo4jclient

我正在使用Neo4j进行使用官方Neo4j驱动程序for .NET的项目:

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

这个驱动程序适用于bolt协议,我的假设是专用二进制协议比HTTP API更有效。但是从项目开始以来,我注意到Neo4j的延迟相对较高,即使是非常简单的操作也是如此。例如,当UserID是索引字段且数据库完全为空时,以下匹配为30-60ms:

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

在我的本地计算机(接近零网络开销)和我们的生产环境中都会发生此行为。我今天开始调查此问题并发现查询很快返回,但实际流式传输结果需要很长时间。例如,下面的查询在localhost上调用返回之前需要 0.2ms ,然后在ToArray()上调用result(缓冲记录,在这种情况下是一个整数field)将时间增加到 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(); 
    }
}

然后我尝试了社区赞助的Neo4jClient软件包,它可以在HTTP上运行:

https://github.com/Readify/Neo4jClient

使用相同的查询,总时间减少到只有0.5毫秒:

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();

运行更官方的基准测试会得到以下结果,螺栓驱动的官方驱动程序和基于HTTP的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 |

当网络开销可以忽略不计时,HTTP客户端 160x

我还在我们的生产环境中运行基准测试,虽然差异不是很大,但HTTP方法仍然快6倍(我的生产网络连接非常慢)。

完整的基准代码:

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();
      }
    }
}

我的机器和生产都运行Neo4j CE 3.0.4(目前是社区版),虽然我在Windows 10上运行它并且生产是Linux机器。我们没有根据我的知识调整任何设置,但我怀疑这可以解释160倍的性能差异。

我也尝试重用会话对象(我认为这是一个非常糟糕的主意,因为它不是线程安全的)因为创建会话涉及创建一个事务,看看是否有所作为,但它不是显。

我希望我可以使用Neo4jClient,但我们真的需要能够执行任意字符串查询,而Neo4jClient在很大程度上依赖于流畅的API,虽然它提供了低级别的字符串模式,但它已被弃用且{{3} }。

1 个答案:

答案 0 :(得分:4)

在进一步挖掘之后,我特意将问题追溯到Neo4j.Driver软件包,因为NodeJS的驱动程序没有遇到同样的问题。

克隆包的当前source,构建它并直接引用DLL而不是NuGet包完全消除了这个问题。为了正确看待:NuGet(1.0.2)上的当前版本需要 62秒来对localhost执行1000个简单匹配请求,而当前源在 0.3秒内执行此操作 strong>(甚至将NodeJS驱动程序击败10倍)。

我不太清楚为什么,但我很确定它与当前包的rda.SocketsForPCL依赖关系有关,它似乎是一个胶合库,使套接字能够跨平台工作。但是,当前源会引用System.Net.Sockets包。

总而言之,这个问题可以通过引用源的当前版本来解决,并且在发布新版本的软件包时将完全解决。