4

我的设置:

Java(TM) SE Runtime Environment (build 1.7.0_45-b18)
Java HotSpot(TM) 64-Bit Server VM (build 24.45-b08, mixed mode)
Neo4j 2.0.0-M06 Enterprise

首先,我确保通过执行以下命令来预热缓存:

START n=node(*) RETURN COUNT(n);
START r=relationship(*) RETURN count(r);

表的大小为 63,677 个节点和 7,169,995 个关系

现在我有以下查询:

START u1=node:node_auto_index('uid:39')
MATCH (u1:user)-[w:WANTS]->(c:card)<-[h:HAS]-(u2:user)
WHERE u2.uid <> 39
WITH u2.uid AS uid, (CASE WHEN w.qty < h.qty THEN w.qty ELSE h.qty END) AS have
RETURN uid, SUM(have) AS total
ORDER BY total DESC
SKIP 0
LIMIT 25

这个 UID 有大约 40k+ 个结果,我希望能够对其进行分页。最初的跳过大约在773ms. 我尝试了第 2 页(跳过第 25 页),即使到第 500 页,延迟也差不多,它只上升到了,900ms所以我并没有真正打扰。现在我尝试了一些快进分页并跳跃了数千,所以我做了 1000,然后是 2000,然后是 3000。我希望 ORDER BY 安排已经被 Neo4j 缓存,并且使用SKIP只会移动到结果中的那个索引并且不会有再次遍历每一个。但是对于每千次跳过,我使延迟增加了很多。这不仅仅是缓存预热,因为对于一个我已经预热了缓存和两个,我为每个跳过尝试了几次相同的跳过,它产生了相同的结果:

SKIP    0:  773ms
SKIP 1000: 1369ms
SKIP 2000: 2491ms
SKIP 3000: 3899ms
SKIP 4000: 5686ms
SKIP 5000: 7424ms

现在谁会想查看 5000 页的结果呢?甚至40k?!:) 好点子!我可能会限制用户可以查看的最大结果,但我只是对这种现象感到好奇。有人能解释一下为什么 Neo4j 似乎在重复它似乎已经知道的东西吗?

这是我对 0 跳过的分析:

==> ColumnFilter(symKeys=["uid", "  INTERNAL_AGGREGATE65c4d6a2-1930-4f32-8fd9-5e4399ce6f14"], returnItemNames=["uid", "total"], _rows=25, _db_hits=0)
==> Slice(skip="Literal(0)", _rows=25, _db_hits=0)
==>   Top(orderBy=["SortItem(Cached(  INTERNAL_AGGREGATE65c4d6a2-1930-4f32-8fd9-5e4399ce6f14 of type Any),false)"], limit="Add(Literal(0),Literal(25))", _rows=25, _db_hits=0)
==>     EagerAggregation(keys=["uid"], aggregates=["(  INTERNAL_AGGREGATE65c4d6a2-1930-4f32-8fd9-5e4399ce6f14,Sum(have))"], _rows=41659, _db_hits=0)
==>       ColumnFilter(symKeys=["have", "u1", "uid", "c", "h", "w", "u2"], returnItemNames=["uid", "have"], _rows=146826, _db_hits=0)
==>         Extract(symKeys=["u1", "c", "h", "w", "u2"], exprKeys=["uid", "have"], _rows=146826, _db_hits=587304)
==>           Filter(pred="((NOT(Product(u2,uid(0),true) == Literal(39)) AND hasLabel(u1:user(0))) AND hasLabel(u2:user(0)))", _rows=146826, _db_hits=146826)
==>             TraversalMatcher(trail="(u1)-[w:WANTS WHERE (hasLabel(NodeIdentifier():card(1)) AND hasLabel(NodeIdentifier():card(1))) AND true]->(c)<-[h:HAS WHERE (NOT(Product(NodeIdentifier(),uid(0),true) == Literal(39)) AND hasLabel(NodeIdentifier():user(0))) AND true]-(u2)", _rows=146826, _db_hits=293696)

对于 5000 跳过:

==> ColumnFilter(symKeys=["uid", "  INTERNAL_AGGREGATE99329ea5-03cd-4d53-a6bc-3ad554b47872"], returnItemNames=["uid", "total"], _rows=25, _db_hits=0)
==> Slice(skip="Literal(5000)", _rows=25, _db_hits=0)
==>   Top(orderBy=["SortItem(Cached(  INTERNAL_AGGREGATE99329ea5-03cd-4d53-a6bc-3ad554b47872 of type Any),false)"], limit="Add(Literal(5000),Literal(25))", _rows=5025, _db_hits=0)
==>     EagerAggregation(keys=["uid"], aggregates=["(  INTERNAL_AGGREGATE99329ea5-03cd-4d53-a6bc-3ad554b47872,Sum(have))"], _rows=41659, _db_hits=0)
==>       ColumnFilter(symKeys=["have", "u1", "uid", "c", "h", "w", "u2"], returnItemNames=["uid", "have"], _rows=146826, _db_hits=0)
==>         Extract(symKeys=["u1", "c", "h", "w", "u2"], exprKeys=["uid", "have"], _rows=146826, _db_hits=587304)
==>           Filter(pred="((NOT(Product(u2,uid(0),true) == Literal(39)) AND hasLabel(u1:user(0))) AND hasLabel(u2:user(0)))", _rows=146826, _db_hits=146826)
==>             TraversalMatcher(trail="(u1)-[w:WANTS WHERE (hasLabel(NodeIdentifier():card(1)) AND hasLabel(NodeIdentifier():card(1))) AND true]->(c)<-[h:HAS WHERE (NOT(Product(NodeIdentifier(),uid(0),true) == Literal(39)) AND hasLabel(NodeIdentifier():user(0))) AND true]-(u2)", _rows=146826, _db_hits=293696)

唯一的区别是 Top 函数上的 LIMIT 子句。我希望我们可以按预期完成这项工作,我真的不想深入研究为 web 应用程序做一个嵌入式 Neo4j + 我自己的 Jetty REST API。

4

1 回答 1

2

结果不会被缓存,否则服务器内的大量内存将保留很可能未使用的结果。

正如你所说的那样,人们最感兴趣的是前两页,然后细化他们的搜索。

如果您需要更可预测的分页性能,请首先从 neo 中提取更多结果,将它们粘贴到您的用户会话中并从那里提供它们。您可以使用比数据库更多的上下文信息(例如用户行为配置文件或高级用户标志等)来做到这一点。

于 2013-10-25T23:59:01.593 回答