【问题标题】:Performance in Neo4j cypher queryNeo4j 密码查询中的性能
【发布时间】:2014-11-18 20:01:53
【问题描述】:

我有以下密码查询:

MATCH (country:Country { name: 'norway' }) <- [:LIVES_IN] - (person:Person)
WITH person
MATCH (skill:Skill { name: 'java' }) <- [:HAS_SKILL] - (person)
WITH person 
OPTIONAL MATCH (skill:Skill { name: 'javascript' }) <- [rel:HAS_SKILL] - (person)
WITH person, CASE WHEN skill IS NOT NULL THEN 1 ELSE 0 END as matches 
ORDER BY matches DESC 
LIMIT 50 
RETURN COLLECT(ID(person)) as personIDs

添加更多节点时性能似乎更差。现在只有 5000 个 Person 节点(一个 Person 节点可以与 Skill 节点有多个 HAS_SKILL 关系)。现在执行查询大约需要 180 毫秒,但是添加另外 1000 个具有关系的 Person 节点会为查询增加 30-40 毫秒。我们计划拥有数百万个 Person 节点,因此每 1000 个 Person 增加 40 毫秒是行不通的。

我在查询中使用参数,而不是在上述查询中使用“norway”、“java”、“javascript”。我在 :Country(name) 和 :Skill(name) 上创建了索引。

我的查询目标是匹配居住在指定国家(挪威)的每个人,这些人也具有“java”技能。如果此人还具有“javascript”技能,则应在结果中对其进行排序。

如何重组查询以提高性能?

编辑:

如果我切换出去,:Country 节点似乎也有问题

MATCH (country:Country { name: 'norway' }) <- [:LIVES_IN] - (person:Person)

MATCH (city:City { name: 'vancouver' }) <- [:LIVES_IN] - (person:Person)

查询时间会下降到大约 15-50 毫秒,具体取决于我查询的城市。添加更多节点时,查询时间仍然明显增加。

编辑 2:

当第一个匹配子句中有很多行时,我似乎查询时间增加了很多。因此,如果我先将查询切换为在技能节点上匹配,则查询时间会大大减少。该查询是 API 的一部分,它是动态创建的,我不知道哪个匹配子句将返回最少的行。当数据库增长时,每个匹配子句中的行数也可能会更多。

编辑 3

我已经对答案进行了一些测试,现在我有以下查询:

MATCH (country:Country { name: 'norway'}) 
WITH country 
MATCH (country) <- [:LIVES_IN] - (person:Person) 
WITH person 
MATCH (person) - [:HAS_SKILL] -> (skill:Skill) WHERE skill.name = 'java' 
MATCH (person) - [:MEMBER_OF_GROUP] -> (group:Group) WHERE group.name = 'some_group_name' 
RETURN DISTINCT ID(person) as id 
LIMIT 50 

这仍然存在性能问题,首先匹配所有技能等是否更好,例如与 Country 节点?查询也可以变得更大,我可能需要添加针对多个技能、组、项目等的匹配。

编辑 4

我稍微修改了查询,似乎这成功了。我现在首先匹配所有需要的技能、公司、团体、国家等。然后在查询中稍后使用它们。在分析器中,这将数据库命中从 700k 减少到 188 或其他东西。它与我的原始查询(不同的标记节点等)略有不同,但它解决了相同的问题。我想这可以通过首先在具有最少关系的节点上匹配等来进一步改进,从减少节点数量开始。稍后我会进行更多测试!

MATCH (company:Company { name: 'relinkgroup' }) 
WITH company 
MATCH (skill:Skill { name: 'java' }) 
WITH company, skill 
MATCH (skill2:Skill { name: 'ajax' }) 
WITH company, skill, skill2 
MATCH (country:Country { name: 'canada' }) 
WITH company, skill, skill2, country 
MATCH (company) <- [:WORKED_AT] - (person:Person) 
, (person) - [:HAS_SKILL] -> (skill) 
, (person) - [:HAS_SKILL] -> (skill2) 
, (person) - [:LIVES_IN] -> (country) 
RETURN DISTINCT ID(person) as id 
LIMIT 50

【问题讨论】:

    标签: neo4j cypher


    【解决方案1】:

    对于查询的第一行,执行必须查找国家和个人之间的所有可能路径。限制您的初始匹配(从而为遍历定义更准确的起点)您将赢得一些性能。

    所以不是

    MATCH (country:Country { name: 'norway' }) <- [:LIVES_IN] - (person:Person)
    

    尝试分两步进行:

    MATCH (country:Country { name: 'norway' })
    WITH country
    MATCH (country)<-[:LIVES_IN]-(person:Person)
    WITH person
    

    作为示例,我将在 neo4j 控制台中使用简单的电影应用程序:http://console.neo4j.org/

    执行与您相同的查询以查找知道 cypher 的人:

     MATCH (n:Crew)-[r:KNOWS]-m WHERE n.name='Cypher' RETURN n, m
    

    执行计划将是:

    Execution Plan
    ColumnFilter
      |
      +Filter
        |
        +TraversalMatcher
    
    +------------------+------+--------+-------------+----------------------------------------+
    |         Operator | Rows | DbHits | Identifiers |                                  Other |
    +------------------+------+--------+-------------+----------------------------------------+
    |     ColumnFilter |    2 |      0 |             |                      keep columns n, m |
    |           Filter |    2 |     14 |             | Property(n,name(0)) == {  AUTOSTRING0} |
    | TraversalMatcher |    7 |     16 |             |                                m, r, m |
    +------------------+------+--------+-------------+----------------------------------------+
    
    Total database accesses: 30
    

    并通过定义一个准确的起点:

     MATCH (n:Crew) WHERE n.name='Cypher' WITH n MATCH (n)-[:KNOWS]-(m) RETURN n,m
    

    导致以下执行计划:

    Execution Plan
    ColumnFilter
      |
      +SimplePatternMatcher
        |
        +Filter
          |
          +NodeByLabel
    
    +----------------------+------+--------+-------------------+----------------------------------------+
    |             Operator | Rows | DbHits |       Identifiers |                                  Other |
    +----------------------+------+--------+-------------------+----------------------------------------+
    |         ColumnFilter |    2 |      0 |                   |                      keep columns n, m |
    | SimplePatternMatcher |    2 |      0 | m, n,   UNNAMED53 |                                        |
    |               Filter |    1 |      8 |                   | Property(n,name(0)) == {  AUTOSTRING0} |
    |          NodeByLabel |    4 |      5 |              n, n |                                  :Crew |
    +----------------------+------+--------+-------------------+----------------------------------------+
    
    Total database accesses: 13
    

    如您所见,第一种方法使用遍历模式,这对于节点数量来说是相当昂贵的,而且您正在图上进行全局匹配。

    第二个使用明确的起点,使用标签索引。

    编辑

    对于技能部分,我会做这样的事情,如果你有一些测试数据可以提供它可能对测试更有帮助:

    MATCH (country:Country { name: 'norway' })
    WITH country
    MATCH (country)<-[:LIVES_IN]-(person:Person)-[:HAS_SKILL]->(skill:Skill)
    WHERE skill.name = 'java'
    WITH person
    OPTIONAL MATCH (person)-[:HAS_SKILL]->(skillb:Skill) WHERE skillb.name = 'javascript'
    WITH person, skillb
    

    不需要全局查找,因为他已经找到了人,他只需遵循“HAS_SKILL”关系并过滤技能名称值

    编辑 2:

    关于您上次的编辑,也许是查询的最后一部分:

    MATCH (company) <- [:WORKED_AT] - (person:Person) 
    , (person) - [:HAS_SKILL] -> (skill) 
    , (person) - [:HAS_SKILL] -> (skill2) 
    , (person) - [:LIVES_IN] -> (country) 
    

    最好写成:

    MATCH (person:Person)-[:WORKED_AT]->(company)
    WHERE (person)-[:HAS_SKILL]->(skill)
    AND (person)-[:HAS_SKILL]->(skill2)
    AND (person)-[:LIVES_IN]->(country)
    

    【讨论】:

    • 这看起来很有希望,它显着提升了性能。当我需要 Person 还具备“java”等技能时,我将如何构建这样的查询?
    • 好吧,你已经定义了你的起点,现在你可以用人来计算技能的关系,并为技能的名称添加一个 WHERE 子句,而不是寻找所有的技能,我将用技能的一部分
    • 好的,我尝试了您的编辑的一些变体,但在第二场比赛后表现不佳。请参阅我的问题中的编辑 3。不知道如何提供测试数据。
    • 我想我的编辑 4 得到了它。感谢您帮助我找到一个明确的起点!请随时更新您的答案,我会将其标记为正确!
    • 很高兴它帮助了你,我已经为你的最后一次编辑做了最后一次编辑:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-22
    • 2013-09-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多