让我的Neo4j查询更快

时间:2015-02-05 20:15:14

标签: neo4j cypher

我正在为我们的应用程序评估Neo4j,现在正处于性能问题的阶段。我已经创建了很多节点和边缘,我正在做一些查询。以下是此数据库中节点和边数据的详细信息:

Detail of nodes/edges in database

我正在尝试进行遍历此图表的黄色箭头的搜索。到目前为止,我得到的是以下查询:

MATCH (n:LABEL_TYPE_Project {id:'14'})
    -[:RELATIONSHIP_scopes*1]->(m:LABEL_TYPE_PermissionNode)
    -[:RELATIONSHIP_observedBy*1]->(o:LABEL_TYPE_Item)
WHERE m.id IN ['1', '2', '6', '12', '12064', '19614', '19742', '19863', '21453', '21454', '21457', '21657', '21658', '31123', '31127', '31130', '47691', '55603', '55650', '56026', '56028', '56029', '56050', '56052', '85383', '85406', '85615', '105665', '1035242', '1035243']
   AND o.content =~ '.*some string.*'
RETURN o
LIMIT 20

(上面的变量路径已更新,请参阅“更新2”)

以上查询需要几乎不可接受的1200毫秒。它只返回请求的20个项目。如果我想要相同的计数,这需要永远:

MATCH ... more of the same ...
RETURN count(o)

以上查询需要很多分钟。这是在CentOS上运行的Neo4j 2.2.0-M03社区。大约有385,000个节点,170,000个类型的项目。

我已在所有id字段(以编程方式,index().forNodes(...).add(...))创建了索引,也在内容字段(CREATE INDEX ...语句)上创建了索引。

我的查询还有根本性的改进吗?我能尝试的事情吗?

非常感谢。

根据他们的建议,这个问题已经从Google上的Neo4j讨论组转移过来。


更新1

根据要求:

:schema

给出:

Indexes
  ON :LABEL_TYPE_Item(id)             ONLINE  
  ON :LABEL_TYPE_Item(active)         ONLINE  
  ON :LABEL_TYPE_Item(content)        ONLINE  
  ON :LABEL_TYPE_PermissionNode(id)   ONLINE  
  ON :LABEL_TYPE_Project(id)          ONLINE  

No constraints

(已更新,请参阅“更新2”)


更新2

我对查询做了以下值得注意的改进:

  • 对我感到羞耻,我确实为所有TYPE_Project设置了超级节点(不是设计,只是搞砸了我正在使用的导入算法)而我现在已将其删除了
  • 我有很多“字符串”可能是正确的数据类型,例如整数,布尔值,我现在正在导入它们(你会在下面的更新查询中看到我删除了很多引号)
  • 正如所指出的,我有可变长度的路径,我修复了那些
  • 正如所指出的,我应该有唯一性索引而不是常规索引,我修复了

结果:

:schema

现在给出:

Indexes
  ON :LABEL_TYPE_Item(active)         ONLINE                             
  ON :LABEL_TYPE_Item(content)        ONLINE                             
  ON :LABEL_TYPE_Item(id)             ONLINE (for uniqueness constraint) 
  ON :LABEL_TYPE_PermissionNode(id)   ONLINE (for uniqueness constraint) 
  ON :LABEL_TYPE_Project(id)          ONLINE (for uniqueness constraint) 

Constraints
  ON (label_type_item:LABEL_TYPE_Item) ASSERT label_type_item.id IS UNIQUE
  ON (label_type_project:LABEL_TYPE_Project) ASSERT label_type_project.id IS UNIQUE
  ON (label_type_permissionnode:LABEL_TYPE_PermissionNode) ASSERT label_type_permissionnode.id IS UNIQUE

查询现在看起来像这样:

MATCH (n:LABEL_TYPE_Project {id:14})
   -[:RELATIONSHIP_scopes]->(m:LABEL_TYPE_PermissionNode)
   -[:RELATIONSHIP_observedBy]->(o:LABEL_TYPE_Item)
WHERE m.id IN [1, 2, 6, 12, 12064, 19614, 19742, 19863, 21453, 21454, 21457, 21657, 21658, 31123, 31127, 31130, 47691, 55603, 55650, 56026, 56028, 56029, 56050, 56052, 85383, 85406, 85615, 105665, 1035242, 1035243]
   AND o.content =~ '.*some string.*'
RETURN o
LIMIT 20

以上查询现在需要约。 350毫秒。

我仍然想要相同的数量:

MATCH ...
RETURN count(0)

以上查询现在需要约。 1100毫秒。虽然这个更好,而且对于这个特定的查询几乎不可接受,但我已经发现了一些本质上需要更长时间的更复杂的查询。因此,对此查询的进一步改进将非常棒。

此处要求的是PROFILE RETURN o查询(针对改进后的查询):

Compiler CYPHER 2.2

Planner COST

Projection
  |
  +Limit
    |
    +Filter(0)
      |
      +Expand(All)(0)
        |
        +Filter(1)
          |
          +Expand(All)(1)
            |
            +NodeUniqueIndexSeek

+---------------------+---------------+-------+--------+-------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|            Operator | EstimatedRows |  Rows | DbHits | Identifiers |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            Other |
+---------------------+---------------+-------+--------+-------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|          Projection |          1900 |    20 |      0 |     m, n, o |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                o |
|               Limit |          1900 |    20 |      0 |     m, n, o |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    {  AUTOINT32} |
|           Filter(0) |          1900 |    20 | 131925 |     m, n, o |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  (hasLabel(o:LABEL_TYPE_Item) AND Property(o,content(23)) ~= /{  AUTOSTRING31}/) |
|      Expand(All)(0) |          4993 | 43975 |  43993 |     m, n, o |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              (m)-[:RELATIONSHIP_observedBy]->(o) |
|           Filter(1) |             2 |    18 |    614 |        m, n | (hasLabel(m:LABEL_TYPE_PermissionNode) AND any(-_-INNER-_- in Collection(List({  AUTOINT1}, {  AUTOINT2}, {  AUTOINT3}, {  AUTOINT4}, {  AUTOINT5}, {  AUTOINT6}, {  AUTOINT7}, {  AUTOINT8}, {  AUTOINT9}, {  AUTOINT10}, {  AUTOINT11}, {  AUTOINT12}, {  AUTOINT13}, {  AUTOINT14}, {  AUTOINT15}, {  AUTOINT16}, {  AUTOINT17}, {  AUTOINT18}, {  AUTOINT19}, {  AUTOINT20}, {  AUTOINT21}, {  AUTOINT22}, {  AUTOINT23}, {  AUTOINT24}, {  AUTOINT25}, {  AUTOINT26}, {  AUTOINT27}, {  AUTOINT28}, {  AUTOINT29}, {  AUTOINT30})) where Property(m,id(0)) == -_-INNER-_-)) |
|      Expand(All)(1) |            11 |    18 |     19 |        m, n |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  (n)-[:RELATIONSHIP_scopes]->(m) |
| NodeUniqueIndexSeek |             1 |     1 |      1 |           n |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          :LABEL_TYPE_Project(id) |
+---------------------+---------------+-------+--------+-------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

以下是PROFILE RETURN count(o)查询(针对改进后的查询):

Compiler CYPHER 2.2

Planner COST

Limit
  |
  +EagerAggregation
    |
    +Filter(0)
      |
      +Expand(All)(0)
        |
        +Filter(1)
          |
          +Expand(All)(1)
            |
            +NodeUniqueIndexSeek

+---------------------+---------------+--------+--------+-------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|            Operator | EstimatedRows |   Rows | DbHits | Identifiers |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            Other |
+---------------------+---------------+--------+--------+-------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|               Limit |            44 |      1 |      0 |    count(o) |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    {  AUTOINT32} |
|    EagerAggregation |            44 |      1 |      0 |    count(o) |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
|           Filter(0) |          1900 |    101 | 440565 |     m, n, o |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  (hasLabel(o:LABEL_TYPE_Item) AND Property(o,content(23)) ~= /{  AUTOSTRING31}/) |
|      Expand(All)(0) |          4993 | 146855 | 146881 |     m, n, o |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              (m)-[:RELATIONSHIP_observedBy]->(o) |
|           Filter(1) |             2 |     26 |    850 |        m, n | (hasLabel(m:LABEL_TYPE_PermissionNode) AND any(-_-INNER-_- in Collection(List({  AUTOINT1}, {  AUTOINT2}, {  AUTOINT3}, {  AUTOINT4}, {  AUTOINT5}, {  AUTOINT6}, {  AUTOINT7}, {  AUTOINT8}, {  AUTOINT9}, {  AUTOINT10}, {  AUTOINT11}, {  AUTOINT12}, {  AUTOINT13}, {  AUTOINT14}, {  AUTOINT15}, {  AUTOINT16}, {  AUTOINT17}, {  AUTOINT18}, {  AUTOINT19}, {  AUTOINT20}, {  AUTOINT21}, {  AUTOINT22}, {  AUTOINT23}, {  AUTOINT24}, {  AUTOINT25}, {  AUTOINT26}, {  AUTOINT27}, {  AUTOINT28}, {  AUTOINT29}, {  AUTOINT30})) where Property(m,id(0)) == -_-INNER-_-)) |
|      Expand(All)(1) |            11 |     26 |     27 |        m, n |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  (n)-[:RELATIONSHIP_scopes]->(m) |
| NodeUniqueIndexSeek |             1 |      1 |      1 |           n |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          :LABEL_TYPE_Project(id) |
+---------------------+---------------+--------+--------+-------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

剩下的建议:

  • 使用MATCH ... WITH x MATCH ...->(x)语法:到目前为止,这根本没有帮助我
  • 使用Lucene indexes:仍 查看“更新3”中的结果
  • 使用预计算:这对我没有帮助,因为查询将是相当不同的

更新3

我一直在玩全文搜索,并将content属性编入索引,如下所示:

IndexManager indexManager = getGraphDb().index();
Map<String, String> customConfiguration = MapUtil.stringMap(IndexManager.PROVIDER, "lucene", "type", "fulltext");
Index<Node> index = indexManager.forNodes("INDEX_FULL_TEXT_content_Item", customConfiguration);
index.add(node, "content", value);

当我运行以下查询时,这需要大约。 1200ms:

START o=node:INDEX_FULL_TEXT_content_Item("content:*some string*")
MATCH (n:LABEL_TYPE_Project {id:14})
   -[:RELATIONSHIP_scopes]->(m:LABEL_TYPE_PermissionNode)
   -[:RELATIONSHIP_observedBy]->(o:LABEL_TYPE_Item)
WHERE m.id IN [1, 2, 6, 12, 12064, 19614, 19742, 19863, 21453, 21454, 21457, 21657, 21658, 31123, 31127, 31130, 47691, 55603, 55650, 56026, 56028, 56029, 56050, 56052, 85383, 85406, 85615, 105665, 1035242, 1035243]
RETURN count(o);

以下是此查询的PROFILE

Compiler CYPHER 2.2

Planner COST

EagerAggregation
  |
  +Filter(0)
    |
    +Expand(All)(0)
      |
      +NodeHashJoin
        |
        +Filter(1)
        |  |
        |  +NodeByIndexQuery
        |
        +Expand(All)(1)
           |
           +NodeUniqueIndexSeek

+---------------------+---------------+--------+--------+-------------+------------------------------------------------------------------------+
|            Operator | EstimatedRows |   Rows | DbHits | Identifiers |                                                                  Other |
+---------------------+---------------+--------+--------+-------------+------------------------------------------------------------------------+
|    EagerAggregation |            50 |      1 |      0 |    count(o) |                                                                        |
|           Filter(0) |          2533 |    166 |    498 |     m, n, o | (Property(n,id(0)) == {  AUTOINT0} AND hasLabel(n:LABEL_TYPE_Project)) |
|      Expand(All)(0) |         32933 |    166 |    332 |     m, n, o |                                        (m)<-[:RELATIONSHIP_scopes]-(n) |
|        NodeHashJoin |         32933 |    166 |      0 |        m, o |                                                                      o |
|           Filter(1) |             1 |    553 |    553 |           o |                                            hasLabel(o:LABEL_TYPE_Item) |
|    NodeByIndexQuery |             1 |    553 |    554 |           o |               Literal(content:*itzndby*); INDEX_FULL_TEXT_content_Item |
|      Expand(All)(1) |         64914 | 146855 | 146881 |        m, o |                                    (m)-[:RELATIONSHIP_observedBy]->(o) |
| NodeUniqueIndexSeek |            27 |     26 |     30 |           m |                                         :LABEL_TYPE_PermissionNode(id) |
+---------------------+---------------+--------+--------+-------------+------------------------------------------------------------------------+

2 个答案:

答案 0 :(得分:1)

要考虑/尝试的事情:通常在查询优化时,游戏的第一个名称是在回答查询时首先找出考虑较少数据的方法。与考虑更少的数据相比,更快地考虑相同数据的成果远远不够。

    您在content字段上的
  • Lucene indexes。我的理解是你正在做的正则表达式并不是缩小cypher的搜索路径,所以它基本上必须查看每个o:LABEL_TYPE_Item并对该字段运行正则表达式。你的正则表达式只是寻找一个子字符串,所以lucene可能有助于减少密码在它给你一个结果之前必须考虑的节点数。
  • 你的关系路径是可变长度的,(-[:RELATIONSHIP_scopes*1]->)但你给我们的图像表明你只需要一跳。在两种关系跳跃中,根据图表的结构(以及您拥有的数据量),您可能会查看比您需要的信息更多的信息。仔细考虑这些关系跳跃和您的数据模型;你可以用-[:RELATIONSHIP_scopes]->代替吗?请注意,WHERE个节点上有m个子句,您可能会遍历超出所需数量的子句。
  • 检查查询计划(通过PROFILE,google查看文档)。我看到很多人使用的一个技巧是将查询中最严格的部分推到顶部,在WITH块之前。这减少了“起点”的数量。

我的意思是接受此查询...

MATCH (foo)-[:stuff*]->(bar)   // (bunch of other complex stuff)
WHERE bar.id = 5
RETURN foo

把它变成这个:

MATCH bar
WHERE bar.id = 5
WITH bar
MATCH (foo)-[:stuff*]->(bar)
RETURN foo;

(通过PROFILE检查输出,此技巧可用于强制查询执行计划首先执行最具选择性的操作,从而大幅减少密码考虑/遍历的图形数量...更好的性能)

  • 预先计算;如果您拥有一直使用的特定节点集(具有您标识的ID的节点),则可以创建自己的自定义索引节点。我们称之为(foo:SpecialIndex { label: "My Nifty Index" })。这类似于关系数据库中的“视图”。您可以将要快速访问的内容快速链接到foo。然后你的查询,而不是那个大的WHERE id IN [blah blah]子句,它只是查找foo:SpecialIndex,遍历到生命值,然后从那里开始。当您的ID列表中的入口点列表很大,快速增长或两者兼而有之时,此技巧很有效。这保持了你正常做的所有相同的计算,但是提前完成了一些计算,所以每次运行查询时都不要这样做。
  • 该图表中有任何超级节点? (超级节点是一个非常密集连接的节点,即具有一百万个出站关系的节点) - 不要这样做。尝试安排您的数据模型,以便尽可能没有超节点。
  • JVM/Node Cache tweaks。有时,您可以通过更改节点缓存策略或可用内存来进行缓存来获得优势。这里的想法是,不是在磁盘上点击数据,如果你加热缓存,那么你至少可以获得一些I / O.在某些情况下,这可以提供帮助,但除非您配置JVM或neo4j的方式已经有点内存不足,否则它不会是我的第一站。这个可能也会帮助你少一点,因为它试图让你当前的访问模式更快,而不是改善你的实际访问模式。

答案 1 :(得分:1)

你可以在浏览器中分享你的输出吗?

如果你没有这样做:

create constraint on (p:LABEL_TYPE_Project) assert p.id is unique;
create constraint on (m:LABEL_TYPE_PermissionNode) assert m.id is unique;

如果您使用FULLTEXT_CONFIG对其进行索引,然后使用Item.content

,那么您创建的手动索引仅对START o=node:items("content:(some string)") MATCH ...有帮助

与Neo4j一样,你总是可以在两个方向上遍历关系,你不需要反向关系,它只会伤害性能,因为查询往往会更多地检查一个周期。

您的查询中不需要可变长度路径[*1],请将其更改为:

MATCH (n:LABEL_TYPE_Project {id:'14'})-[:RELATIONSHIP_scopes]->
      (m:LABEL_TYPE_PermissionNode)-[:RELATIONSHIP_observedBy]->(o:LABEL_TYPE_Item)
WHERE m.id in ['1', '2', ... '1035242', '1035243']
AND o.content =~ '.*itzndby.*' RETURN o LIMIT 20

对于实际查询,请使用参数,用于project-id和permission.id - &gt;

MATCH (n:LABEL_TYPE_Project {id: {p_id}})-[:RELATIONSHIP_scopes]->(m:LABEL_TYPE_PermissionNode)-[:RELATIONSHIP_observedBy]->(o:LABEL_TYPE_Item) 
WHERE m.id in {m_ids} AND o.content =~ '.*'+{item_content}+'.*'
RETURN o LIMIT 20

请记住,现实的查询性能仅显示在预热的系统上,因此请至少运行两次查询

您可能还想拆分查询

MATCH (n:LABEL_TYPE_Project {id: {p_id}})-[:RELATIONSHIP_scopes]->(m:LABEL_TYPE_PermissionNode)
WHERE m.id in {m_ids} 
              WITH distinct m
              MATCH (m)-[:RELATIONSHIP_observedBy]->(o:LABEL_TYPE_Item) 
WHERE o.content =~ '.*'+{item_content}+'.*'
RETURN o LIMIT 20

还要了解PROFILE您可以在旧的webadmin中为查询添加前缀:http://localhost:7474/webadmin/#/console/

如果您使用Neo4j 2.2-M03,则内置支持EXPLAINPROFILE前缀的查询计划可视化。