1

这个问题类似于这两个:1628344115456345

更新:这是一个数据库转储

在 190K 节点和 727K 关系(以及 128MB 的数据库磁盘使用量)的数据库中,我想运行以下查询

START start_node=node(<id>) 
MATCH (start_node)-[r:COOCCURS_WITH]-(partner), 
      (partner)-[s:COOCCURS_WITH]-(another_partner)-[:COOCCURS_WITH]-(start_node)  
RETURN COUNT(DISTINCT s) as num_partner_partner_links;

在这个数据库中,90% 的节点有 0 个关系,剩下的 10% 有 1 到 670 个,所以这个查询可以返回的最大网络不可能有超过 220K 的链接 (670*670)/2)。

在具有少于 10K partner_partner_links 的节点上,查询需要 2-4 秒,当蠕虫起来时。对于更多连接的节点(20-45K 链接),大约需要 40-50 秒(不知道连接最多的节点需要多少时间)。

指定关系方向有一点帮助,但作用不大(但是查询没有返回我需要它返回的内容)。

在最大节点之一上分析查询说:

==> ColumnFilter(symKeys=["  INTERNAL_AGGREGATE48d9beec-0006-4dae-937b-9875f0370ea6"], returnItemNames=["num_partner_links"], _rows=1, _db_hits=0)
==> EagerAggregation(keys=[], aggregates=["(  INTERNAL_AGGREGATE48d9beec-0006-4dae-937b-9875f0370ea6,Distinct)"], _rows=1, _db_hits=0)
==>   PatternMatch(g="(partner)-['r']-(start_node)", _rows=97746, _db_hits=34370048)
==>     TraversalMatcher(trail="(start_node)-[  UNNAMED3:COOCCURS_WITH WHERE true AND true]-(another_partner)-[s:COOCCURS_WITH WHERE true AND true]-(partner)", _rows=116341, _db_hits=117176)
==>       ParameterPipe(_rows=1, _db_hits=0)
neo4j-sh (0)$ 

我不明白为什么会这么慢,大多数东西都应该在 RAM 中。是否有可能在 100 毫秒内完成,或者 neo4j 达不到这个要求?如果有帮助的话,我可以把整个数据库放在某个地方。

最大的困惑是相同的查询在重写以使用不同的节点符号时运行速度较慢:)

START n=node(36) 
MATCH (n)-[r:COOCCURS_WITH]-(m), 
      (m)-[s:COOCCURS_WITH]-(p)-[:COOCCURS_WITH]-(n) 
RETURN COUNT(DISTINCT s) AS num_partner_partner_links;

START start_node=node(36) 
MATCH (start_node)-[r:COOCCURS_WITH]-(partner), 
      (partner)-[s:COOCCURS_WITH]-(another_partner)-[:COOCCURS_WITH]-(start_node)  
RETURN COUNT(DISTINCT s) AS num_partner_partner_links;

前者总是在 +4.2 秒内运行,而后者在 3.8 秒以下,无论我一个又一个(交错)运行多少次!?

SW/HW 详细信息:(高级)Neo4j v1.9.RC2,JDK 1.7.0.10,带有 SSD 磁盘、8GBRAM、2 核 i7 的 macbook pro,具有以下 neo4j 配置:

neostore.nodestore.db.mapped_memory=550M
neostore.relationshipstore.db.mapped_memory=540M
neostore.propertystore.db.mapped_memory=690M
neostore.propertystore.db.strings.mapped_memory=430M
neostore.propertystore.db.arrays.mapped_memory=230M
neostore.propertystore.db.index.keys.mapped_memory=150M
neostore.propertystore.db.index.mapped_memory=140M

wrapper.java.initmemory=4092 
wrapper.java.maxmemory=4092
4

2 回答 2

0

将您的查询更改为以下查询。在我的笔记本电脑上,其规格比你的要低得多,执行时间减半。

START start_node=node(36) 
MATCH (start_node)-[r:COOCCURS_WITH]-(partner)
WITH start_node, partner
MATCH (partner)-[s:COOCCURS_WITH]-(another_partner)-[:COOCCURS_WITH]-(start_node)
RETURN COUNT(DISTINCT s) AS num_partner_partner_links;

此外,与默认设置相比,使用您的设置不会对性能产生太大影响。恐怕你得不到你想要的性能,但这个查询是朝着正确方向迈出的一步。

通常遍历 API 会比 Cypher 更快,因为您显式控制遍历。我模仿了如下查询:

public class NeoTraversal {

public static void main(final String[] args) {
    final GraphDatabaseService db = new GraphDatabaseFactory()
            .newEmbeddedDatabaseBuilder("/neo4j")
            .loadPropertiesFromURL(NeoTraversal.class.getClassLoader().getResource("neo4j.properties"))
            .newGraphDatabase();
    final Set<Long> uniquePartnerRels = new HashSet<Long>();
    long startTime = System.currentTimeMillis();
    final Node start = db.getNodeById(36);
    for (final Path path : Traversal.description()
            .breadthFirst()
            .relationships(Rel.COOCCURS_WITH, Direction.BOTH)
            .uniqueness(Uniqueness.NODE_GLOBAL)
            .evaluator(Evaluators.atDepth(1))
            .traverse(start)) {
        Node partner = start.equals(path.startNode()) ? path.endNode() : path.startNode();
        for (final Path partnerPath : Traversal.description()
                .depthFirst()
                .relationships(Rel.COOCCURS_WITH, Direction.BOTH)
                .uniqueness(Uniqueness.RELATIONSHIP_PATH)
                .evaluator(Evaluators.atDepth(2))
                .evaluator(Evaluators.includeWhereEndNodeIs(start))
                .traverse(partner)) {
            uniquePartnerRels.add(partnerPath.relationships().iterator().next().getId());
        }
    }
    System.out.println("Execution time: " + (System.currentTimeMillis() - startTime));
    System.out.println(uniquePartnerRels.size());
}

static enum Rel implements RelationshipType {
    COOCCURS_WITH
}

}

这显然优于密码查询,因此这对您来说可能是一个不错的选择。优化可能仍然是可能的。

于 2013-05-13T12:22:57.700 回答
0

似乎除了深度/广度优先遍历之外的任何东西,neo4j 并不是那么“快如闪电”。我通过预先计算所有网络并将它们存储到 MongoDB 中解决了这个问题。描述网络的节点文档如下所示:

{
    node_id : long, 
    partners : long[],
    partner_partner_links : long[]
}

Partners 和 partner_partner_links 是描述 egdes 的文档的 id。获取整个网络需要 2 个查询:一个用于此文档,另一个用于边缘属性(也包含节点属性):

db.edge.find({"_id" : {"$in" : network.partner_partner_links}});
于 2013-05-22T09:13:44.873 回答