我一直在评估Neo4j 1.9.M03一段时间了,并且已经达到了我没想到的水平。
我有一个约140,000个顶点的图表。我也有三类边缘,我们称之为父亲,母亲和丈夫。每个班级大约有80,000个边缘。没有属性,也没有索引。顶点存储大小约为1.3 MB,边缘存储大约为8 MB。
数据源自SQL Server,并且已知从SQL迁移到Neo4j的质量是正确的。已经为几十个顶点对运行了SQL最短路径存储过程,以便知道最短路径距离和路径。
最短路径查询是Cypher:START one=node(0), two=node(1234) MATCH p = shortestPath(one-[*..1000]-two) RETURN p;
PARTIAL TEST CASE ONE:我只使用丈夫和父亲关系,周期的发生(例如v[0] -> v[1] -> v[2] -> v[0])
)很低。如果我在特定的已知长路径上执行最短路径计算(例如,已知为~450跳),它在50ms内返回(非缓存),路径为~550跳。由于我们排除了一部分边缘,因此预计会增加长度。
PARTIAL TEST CASE TWO :同样,如果我只使用丈夫和母亲关系,周期的发生(例如v[0] -> v[1] -> v[2] -> v[0])
)很低。如果我执行相同的最短路径,我得到结果与之前的顺序相同:大约50ms(非缓存),路径长度增加类似。
完整测试案例:我使用所有(父亲,母亲和丈夫)关系。由于常见情况v[0] mother-> v[1] husband-> v[2] <-father v[0]
,循环的发生现在可以预测为高。当我执行最短路径查询时,JVM分配4千兆字节的内存,计算无法完成。 这就是问题所在。
我的论点是循环的定期发生导致了这种行为,否则当我只添加另一类父边时,我不会指望性能上有如此巨大的差异 - 除非最短路径算法不考虑周期。
我直接使用Java API应用Dijkstra算法,所有边缘的成本为1,并且获得了与所使用的标准ShortestPath算法类似的结果。因此,我在IntelliJ调试时间6分钟后收到此异常。
Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
at org.neo4j.kernel.impl.util.RelIdArray$RelIdIteratorImpl.<init>(RelIdArray.java:661)
at org.neo4j.kernel.impl.util.RelIdArray$DirectionWrapper$3.iterator(RelIdArray.java:327)
at org.neo4j.kernel.impl.util.RelIdArray.iterator(RelIdArray.java:270)
at org.neo4j.kernel.impl.core.NodeImpl.getAllRelationships(NodeImpl.java:172)
at org.neo4j.kernel.impl.core.NodeImpl.getRelationships(NodeImpl.java:270)
at org.neo4j.kernel.impl.core.NodeProxy.getRelationships(NodeProxy.java:82)
at org.neo4j.kernel.StandardExpander$AllExpander.doExpand(StandardExpander.java:303)
at org.neo4j.kernel.StandardExpander$RelationshipExpansion.iterator(StandardExpander.java:194)
at org.neo4j.kernel.impl.traversal.TraversalBranchImpl.expandRelationshipsWithoutChecks(TraversalBranchImpl.java:114)
at org.neo4j.kernel.impl.traversal.TraversalBranchImpl.expandRelationships(TraversalBranchImpl.java:104)
at org.neo4j.kernel.impl.traversal.TraversalBranchImpl.initialize(TraversalBranchImpl.java:130)
at org.neo4j.kernel.impl.traversal.TraversalBranchImpl.next(TraversalBranchImpl.java:150)
at org.neo4j.graphalgo.impl.util.BestFirstSelectorFactory$BestFirstSelector.next(BestFirstSelectorFactory.java:73)
at org.neo4j.kernel.impl.traversal.TraverserIterator.fetchNextOrNull(TraverserIterator.java:65)
at org.neo4j.kernel.impl.traversal.TraverserIterator.fetchNextOrNull(TraverserIterator.java:34)
at org.neo4j.helpers.collection.PrefetchingIterator.hasNext(PrefetchingIterator.java:55)
at org.neo4j.graphalgo.impl.util.StopAfterWeightIterator.fetchNextOrNull(StopAfterWeightIterator.java:45)
at org.neo4j.graphalgo.impl.util.StopAfterWeightIterator.fetchNextOrNull(StopAfterWeightIterator.java:29)
at org.neo4j.helpers.collection.PrefetchingIterator.hasNext(PrefetchingIterator.java:55)
at org.neo4j.helpers.collection.IteratorUtil.firstOrNull(IteratorUtil.java:51)
at org.neo4j.helpers.collection.IteratorUtil.firstOrNull(IteratorUtil.java:201)
at org.neo4j.graphalgo.impl.path.Dijkstra.findSinglePath(Dijkstra.java:98)
at org.neo4j.graphalgo.impl.path.Dijkstra.findSinglePath(Dijkstra.java:50)
at ShortestPathCalc.Dijkstra(Main.java:198)
at Main.main(Main.java:53)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
你认为我是对的吗?这是图数据库或其最短路径算法的已知限制吗?对我来说,以前访问过的顶点不会存储在哈希表中似乎很愚蠢,因此最短路径算法不会尝试多次尝试路径出先前访问过的顶点。
更新2013年1月25日
Github回购,所以你可以跟进!
https://github.com/squirrelsama/neo4j-shortestpath-issue
更新2013年2月7日
查看接受的答案。简而言之,周期与它无关。
答案 0 :(得分:1)
使用neo4j遍历框架,您可以选择在遍历中使用哪个唯一性,例如RELATIONSHIP_GLOBAL,这样它只能在遍历期间遍历一次关系。这可能会解决您的问题:
// single directional Traversal.traversal( Uniqueness.RELATIONSHIP_GLOBAL ) .evaluator( Evaluators.returnWhereEndNodeIs( myEndNode ) .traverse( myStartNode ); // bi-directional Traversal.bidirectionalTraversal() .mirroredSides( Traversal.traversal( Uniqueness.RELATIONSHIP_GLOBAL ) ) .traverse( myStartNode, myEndNode );
以上示例是主要的,可能需要进行修改才能使用您的查询。
答案 1 :(得分:1)
如果有人试图获得节点44715和17173之间的最短路径,其最短路径已知为112跳,则可以观察到该问题。
如果我们将最短路径评估限制为111跳,则查询会很快完成,但没有路径。 START one=node(44715), two=node(17173) MATCH p = shortestPath(one-[*..111]-two) RETURN p;
但是,如果我们将最短路径评估限制为112跳,我们会观察到查询无法完成,并且JVM会快速分配高达4千兆字节的内存。 START one=node(44715), two=node(17173) MATCH p = shortestPath(one-[*..112]-two) RETURN p;
Neo已经确认这是与要返回的Path对象的程序集有关的边缘案例错误。这是他们的bug积压。
换句话说,周期与问题无关。