【问题标题】:Slow results retrieval from Cypher query in Java - Neo4j 2.0从 Java 中的 Cypher 查询中检索结果缓慢 - Neo4j 2.0
【发布时间】:2014-01-15 13:29:13
【问题描述】:

当我从 Java 中的 Cypher 查询执行中获取结果时,我使用 ResourceIterator<Node> 检索结果的速度出奇地慢。 next() 命令平均耗时 156 毫秒,标准差为 385! 这是预期的行为,还是我做错了什么?任何人都可以提出一种更有效的方法来实现同样的目标吗?


图形结构

我有以下图形布局,其中 Point 节点与其他点有 LinksTo 关系:

节点:点
属性:
- idPoint(此属性的新样式模式唯一约束)
- x(此属性的新样式模式索引)
- y(此属性的新样式模式索引)

Relation:LinksTo
属性:
- idLink
- 长度
(......关系甚至在我的问题中都没有发挥作用......)

图表统计:
- 节点数:890,000
- 关系数:910,000


旧代码

(在 Ubuntu 上使用 Neo4j 2.0.0 stable 和 Oracle Java 7)
(基本上,此代码在给定点周围的 60x60 正方形中搜索节点(点)。)

GraphDatabaseService graphDB = new GraphDatabaseFactory ( ).newEmbeddedDatabase ("points_db");

ExecutionEngine engine = new ExecutionEngine (graphDB);

for (Coordinate c : coords) // coords holds 500 different coordinates
{
    int size = 30;
    int xMin = c.x - size;
    int xMax = c.x + size;
    int yMin = c.y - size;
    int yMax = c.y + size;

    String query = "MATCH (n:POINT) " +
                     "  WHERE n.x > " + xMin +
                     "    AND n.x < " + xMax +
                     "    AND n.y > " + yMin +
                     "    AND n.y < " + yMax +
                     "RETURN n AS neighbour";

    ExecutionResult result = engine.execute (query); // command1

    ResourceIterator<Node> ri = result.columnAs ("neighbour"); // command2

    while (ri.hasNext ( ))
    {
        Node n = ri.next ( ); // command3
        // ... some code ...
    }
}

测量

command1 平均执行时间:7.5 毫秒
command2 平均执行时间: command3 平均执行时间:156 毫秒(标准差为 358)

(使用 500 次迭代(不同坐标)进行的测量,每次迭代平均找到 6 个点。测量是可重复的。)


编辑 1(根据 Luanne 和 Michael 的建议)

新的、更快的参数化代码

(在 Ubuntu 上使用 Neo4j 2.0.0 stable 和 Oracle Java 7)
(基本上,此代码在给定点周围的 60x60 正方形中搜索节点(点)。)

GraphDatabaseService graphDB = new GraphDatabaseFactory ( ).newEmbeddedDatabase ("points_db");

ExecutionEngine engine = new ExecutionEngine (graphDB);
Map<String, Object> params = new HashMap<> ( );

int size = 30;
String query = "MATCH (n:POINT) " +
               "  WHERE n.x > {xMin}" +
               "    AND n.x < {xMax}" +
               "    AND n.y > {yMin}" +
               "    AND n.y < {yMax}" +
               "  RETURN n AS neighbour";

for (Coordinate c : coords) // coords holds 500 different coordinates
{
    params.put ("xMin", (int) c.x - size);
    params.put ("xMax", (int) c.x + size);
    params.put ("yMin", (int) c.y - size);
    params.put ("yMax", (int) c.y + size);

    ExecutionResult result = engine.execute (query, params); // command1

    ResourceIterator<Node> ri = result.columnAs ("neighbour"); // command2

    while (ri.hasNext ( ))
    {
        Node n = ri.next ( ); // command3
        // ... some code ...
    }
}

测量

command1 平均执行时间:1.7 毫秒
command2 平均执行时间: command3 平均执行时间:112 毫秒(标准差为 270)
(使用 500 次迭代(不同坐标)进行的测量,每次迭代平均找到 6 个点。测量是可重复的。)

【问题讨论】:

  • 您可以先参数化您的查询然后测量时间吗? docs.neo4j.org/chunked/milestone/cypher-parameters.html
  • 这里有一个错字:negihbour
  • 参数化的好建议。我改进了代码,新的测量明显更快。但我会说他们仍然在“同一个球场”。

标签: java neo4j cypher


【解决方案1】:

您所做的不是图形查询,而是对整个数据库的范围扫描。

所以它必须拉入所有节点,并为每个节点进行比较。

您通常通过将节点放入将几何编码为二维树结构的树 (r-tree) 来解决此问题,然后您可以访问所需的任何形状,只需 log(levels) 复杂度。

查看有关此主题的 Neo4j 空间演示文稿:

http://neo4j.org/develop/spatial

您还强制 Neo4j 为您的每个节点重新解析和重新构建查询(500 次)。 我同意 Luanne 关于参数化的观点,因此您的查询应该如下所示。 你也应该在for-loop 之前拉这个:

String query = "MATCH (n:POINT) " +
                 "  WHERE n.x > {xMin}" +
                 "    AND n.x < {xMax}" +
                 "    AND n.y > {yMin}" +
                 "    AND n.y < {yMax}" +
                 "  RETURN n AS neighbour";

ExecutionResult result = engine.execute (query,
          map("xMin",xmMin,"xMax",xMax,"yMin",yMin,"yMax",yMax)); // query + params

....

【讨论】:

  • 好吧,如果所有节点都被检查了,那么这里就说明了很多。我天真地假设,如果我查询一个足够小的样本,我在 x 和 y 上的索引将在这里发挥作用(根据我的经验,这将发生在具有 b-tree 或类似索引的关系数据库中......)。
  • 我知道 Neo4j 空间项目。我已经用了大约半年了。但由于与使用 Neo4j 2.0 的轻松新模式样式索引相比,它增加了相当多的复杂性,我希望我可以避免使用它......因为这个查询是我的应用程序中唯一可以从空间 r-tree 索引中受益的地方。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-04-10
  • 1970-01-01
  • 1970-01-01
  • 2014-05-15
  • 1970-01-01
  • 1970-01-01
  • 2012-02-29
相关资源
最近更新 更多