我正在使用Neo4j存储有关电影分级的数据。我想计算两个用户都评价的电影数量。运行查询
$(".diagram-node-task yui3-widget yui3-overlay diagram-node yui3-widget-positioned yui3-widget-stacked").css('width')
它可以在不到一秒钟的时间内完成,但是仍在运行
match (a:User)-[:RATED]->(b:Movie)<-[:RATED]-(c:User) return a,b,c limit 1000
由于堆内存不足,数据库无法完成查询,我将其设置为4gb。我可以正确使用计数功能吗?我不明白这两个查询之间的性能有何显着差异。
答案 0 :(得分:0)
我遇到了类似的情况,并使用以下方法解决了此问题,这将适用于您。
我使用的数据集具有: (TYPE_S)-380个节点 (TYPE_N)-800000个节点 [:S_realation_N]-5600000个关系
查询一个:
match (s:TYPE_S)-[]-(n:TYPE_N) return s, n limit 10
这花了2毫秒。
在db中一旦找到10个模式(关系),neo4j只会返回结果。
查询两个:
match (s:TYPE_S)-[]-(n:TYPE_N) return s, sum(n.value) limit 10
这花费了大约4000毫秒。
这看起来像查询一样快。但肯定会因为涉及聚合而不会像上一个那样快。
原因:
为使查询聚合到模式上,Neo4j必须在执行聚合之前将与给定模式匹配的所有路径(这些路径超过10个或给定限制,根据我的数据集将为5600000)加载到ram中。稍后,此聚合将在10个完整记录的S_TYPE节点上执行,因此现在属于给定限制的指定返回格式。然后将冲头中的其余关系冲洗掉。这意味着一会儿会加载很多数据,由于限制,这些数据稍后将被忽略。
因此,为了在此处优化运行时和内存使用,您必须避免部分查询导致加载数据,该数据随后将被忽略。
这是我对其进行优化的方式:
match (s:TYPE_S) where ((s)-[]-(:TYPE_N))
with collect(s)[0..10] as s_list
unwind s_list as s
match (s)-[]-(n:TYPE_N) return s, sum(n.value)
这花费了64毫秒。
现在neo4j首先列出与TYPE_S有关系的TYPE_S类型的10个节点,然后将模式与这些节点进行匹配并获取其数据。这应该比query2更好地工作,并且运行得更好,因为您要将有限的记录集加载到ram中。
您可以使用这种类似的方法来构建您的查询,方法是使1000个(a,b)不同的用户对短路,然后对其进行汇总。 但是,如果需要通过聚合进行排序,这种方法将失败。
您的查询用完内存的原因是因为您使用的是4 GB RAM,并且正在运行一个查询,该查询可能会将大量组合数据加载到ram中(由于数据组合的多样性,有时可能会超出db的大小)在您的模式中定义的情况下,即使您有50个唯一用户,也可以将50 * 49个可能的唯一模式组合加载到ram中)。另外,其他并行运行的事务和查询也可能会产生影响。
答案 1 :(得分:0)
MissingNumber对发生的事情有很好的解释。当您进行聚合时,必须考虑整个集合正确地进行聚合,并且这必须在LIMIT之前进行,这将对您的堆空间造成巨大的损失。
作为您的替代选择,您可以尝试以下操作:
match (a:User)-[:RATED]->()<-[:RATED]-(c:User)
with DISTINCT a, c
where id(a) < id(c)
limit 1000
match (a)-[:RATED]->(m:Movie)<-[:RATED]-(c)
with a, c, count(m) as moviesRated
return a, moviesRated, c
通过在聚合之前将LIMIT上移,但是使用DISTINCT来确保我们只处理一次该模式中的一对节点(并基于图形ID应用谓词以确保我们从不处理镜像结果),应该获得更有效的查询。然后,针对这1000对a和c中的每对,我们再次扩展模式并获得实际计数。