Neo4j 密码查询性能

Neo4j cypher query perfomance

我分别有以下密码查询及其执行计划,

优化前,

match (o:Order {statusId:74}) <- [:HAS_ORDERS] - (m:Member)
with m,o 
match (m:Member) - [:HAS_WALLET] -> (w:Wallet) where w.currentBalance < 250 
return m as Members,collect(o) as Orders,w as Wallets order by m.createdAt desc limit 10


优化后(db hits减少了40-50%),

match (m:Member) - [:HAS_ORDERS]->(o:Order {statusId:74})
with m, collect(o) as Orders
match (m) - [:HAS_WALLET] - (w:Wallet) where w.currentBalance < 250
return m as Members, Orders, w as Wallets 
order by m.createdAt desc limit 10

有3种类型的节点,会员、订单和钱包。他们之间的关系是这样的,

我有大约 10 万个成员节点(10 万个钱包)和这些成员的近 57 万个订单。 我想获取所有订单状态为 74 且钱包余额小于 250 的会员,上面的查询给出了所需的结果,但平均需要 1.5 秒才能响应。

我怀疑这里还有优化的空间,但我无法弄清楚。我在过滤数据的字段上添加了索引。

我刚刚开始探索 neo4j,不确定如何优化它。

您尝试过子查询吗?如果您可以使用子查询来减少节点数,然后再将其传递给后续查询。 (似乎无所不知的查询规划器可以做到这一点,但 Cypher 还没有。)。您可能需要试验哪个子查询会过滤掉最多的节点。

使用子查询的示例如下: https://community.neo4j.com/t/slow-query-with-very-limited-data-and-boolean-false/31555

另一个在这里: https://community.neo4j.com/t/why-is-this-geospatial-search-so-slow/31952/24

(当然,我假设您已经索引了适当的属性。)

我们可以利用 index-backed ordering 在这里尝试不同的方法。通过提供类型提示(表明 属性 值是字符串的东西)以及索引 属性 的排序,我们可以让规划器使用索引来检查:成员节点的顺序want (by m.createdAt DESC) 免费(这意味着我们不需要检查每个 :Member 节点并对其进行排序),并检查给定顺序中的每个节点以找到满足所需条件的节点,直到我们得到您需要的 10 个。

从 Neo4j 用户 slack 的一些来回讨论中,你提到了你的 100k :Member 节点,其中大约 52k 符合你正在寻找的标准,所以这是一个很好的指标,我们可以在找到符合条件的 10 个之前,不必向下看很远的有序 :Member 节点。

查询如下:

MATCH (m:Member)
WHERE m.createdAt > ''  // type hint
WITH m
ORDER BY m.createdAt DESC
MATCH (m)-[:HAS_WALLET]->(w) 
WHERE w.currentBalance < 250 AND EXISTS {
    MATCH (m)-[:HAS_ORDERS]->(:Order {statusId:74})  
} 
WITH m, w
LIMIT 10
RETURN m as member, w as wallet, [(m)-[:HAS_ORDERS]->(o:Order {statusId:74}) | o] as orders

请注意,通过使用存在子查询,我们只需找到一个满足条件的订单。我们等到达到 10 个成员的限制后,才使用模式理解来抓取 10 个成员的所有订单。