我目前正在开发一个项目,使用neo4j作为数据库和涉及一些硬关系发现的查询,并且在运行性能测试后,我们遇到了一些问题。
我们发现缓存正在疯狂地影响请求的时间(从3000ms到100ms左右)。两次执行相同的请求会导致一个非常慢,而第二个请求会快得多。经过一些搜索后,我们看到了预热方法,它将预加载数据库中的所有节点和关系,查询类似的内容:
match (n)-[r]->() return count(1);
启用缓存加上此预热查询后,我们的查询时间大大减少,但仍然没有查询同一查询的两倍,三倍或四倍的速度。
所以我们继续测试和搜索信息,直到我们看到Neo4j也以某种方式缓冲查询,以便每次都不编译(使用Scala编译器,如果我是对的)。我不知何故说,因为经过激烈的测试,我可以得出结论,Neo4j正在“动态”编译查询。
让我举一个简单的例子说明我的意思:
(数字是 id 属性)
如果我提出如下请求:
match (n:green {id: 1})-[r]->(:red)-[s]->(:green)<-[t]-(m:yellow {id: 7})
return count(m);
我想要做的是查找节点1和节点之间是否存在连接。正如您所看到的,我必须发现一堆节点和更重要的关系,并且编译过程看起来或多或少复杂,因为请求需要1227 ms才能完成。如果我再次提出完全相同的请求,我会得到大约5毫秒的响应时间,足以通过性能测试。绝对是Neo4j或Scala编译器也在缓冲cypher查询。
在理解了cypher请求中存在编译过程之后,我更深入地开始修改已经缓冲的请求的部分内容。更改匹配的最后一个节点的label或id参数也会产生延迟,但只有~19 ms,仍然可以接受:
match (n:green {id: 1})-[r]->(:red)-[s]->(:green)<-[t]-(m:purple {id: 7})
return count(m);
然而,当我重新启动服务器时,进行预热并调整查询以使第一个节点(之前标记为n)不匹配,查询将以0结果快速响应,因此我可以推断出不是解析了所有查询,因为第一个节点不匹配,并且不需要在树中更深入。
我也尝试过使用可选匹配,如果找不到匹配项,则返回null,但它也不起作用。
我想首先询问,到目前为止,我在测试中所说的所有内容都是正确的,如果不是,它是如何实际工作的?其次,在服务器启动时,我应该做什么(如果有办法)在开始时缓存所有内容。不幸的是,项目的要求说查询应该运行良好,甚至第一个(并不是说真实场景有更多的关系和节点,使一切变慢),或者如果没有办法避免这种延迟。
答案 0 :(得分:4)
首先,您需要考虑JVM热身 - 请注意,在需要时(第一次查询)懒惰地加载类,而JIT可能只在几次(数千)调用之后启动。
此
match (n)-[r]->() return count(1);
应该正确地预热节点和关系缓存,但是我不确定它是否还加载了它们的所有属性和索引。还要确保您的数据集符合内存。
直接在cypher查询中提供值,如下所示:{id: 1}
,而不是使用参数{id: {paramId}}
,这意味着当您更改id的值时,需要再次编译查询。
您可以在shell中以这种方式传递参数:
neo4j-sh (?)$ export paramId=5
neo4j-sh (?)$ return {paramId};
==> +-----------+
==> | {paramId} |
==> +-----------+
==> | 5 |
==> +-----------+
==> 1 row
==> 4 ms
因此,如果您需要从头开始执行查询
编辑:添加了如何在shell中传递参数的信息