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

Posted

技术标签:

【中文标题】Neo4j over bolt 协议具有非常高的延迟【英文标题】:Neo4j over bolt protocol has very high latency 【发布时间】:2016-10-14 19:30:55 【问题描述】:

我正在将 Neo4j 用于一个项目,该项目使用此处找到的适用于 .NET 的官方 Neo4j 驱动程序:

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

这个驱动程序在螺栓协议上工作,我的假设是一个专门的二进制协议会比 HTTP API 更有效。但自从项目开始以来,我注意到 Neo4j 的延迟相对较高,即使是非常简单的操作也是如此。例如,当UserID 是索引字段并且数据库完全为空时,如下所示的匹配需要 30-60 毫秒:

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

此行为发生在我的本地计算机(几乎为零的网络开销)和我们的生产环境中。我今天开始对此进行调查,发现查询返回很快,但实际输入结果需要很长时间。例如,以下查询在调用返回 localhost 之前需要 0.2ms,但随后在 result 上调用 ToArray()(缓冲记录,在本例中是单个整数字段)会增加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 客户端的速度快了 160 倍

我还在我们的生产环境中运行了基准测试,虽然差异没有那么大,但 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,虽然它提供了低级字符串模式,但它已被弃用,actively discouraged in the documentation。

【问题讨论】:

我完全愿意使用字符串进行查询,无论是使用您想要的语法进行拉取还是引发错误,我们都可以看到我们能做什么! @ChrisSkardon - 那太好了!如果我能破解一些东西,我会看看。我基本上正在寻找的是 Neo4j 的 Dapper:低级别,输入是字符串和参数,我只是获得将结果映射到对象的帮助。跨度> 【参考方案1】:

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

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

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

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

【讨论】:

以上是关于Neo4j over bolt 协议具有非常高的延迟的主要内容,如果未能解决你的问题,请参考以下文章

如何只用一个端口访问neo4j服务器

透视RPC协议:SOFA-BOLT协议源码分析

如何使用py2neo v4和Neo4j合并节点和关系

如何禁用登录spring数据neo4j

Python返回neo4j的中间路径

使用neo4j JS驱动程序进行套接字挂起