Neo4j最短路径与周期

时间:2013-01-24 20:55:54

标签: database graph path neo4j shortest

我一直在评估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日

查看接受的答案。简而言之,周期与它无关。

2 个答案:

答案 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积压。

换句话说,周期与问题无关。