在我们的neo4j 5.5版数据库中,我们有数据
(c1:Company)-[:ADDRESS_MATCH]-(c2:company)
以及(t:CompanyNameToken)-[:CONTAINS_TOKEN]-(c:Company)
。我们的目标是找到具有地址匹配和通用令牌的公司。执行计划意外改变,如下。仅匹配地址是快速的,并产生以下执行计划,在 14 毫秒内命中 1645 个数据库。
PROFILE MATCH (c1:Company)-[:ADDRESS_MATCH]->(c2:Company)
RETURN c1, c2 LIMIT 100
增加 100 的限制或取消限制时,执行计划保持不变。现在添加令牌匹配作为条件,对于
LIMIT 100
,执行计划仍然只有一个分支,在 58 毫秒内有 250.000 次 DB 命中。
PROFILE MATCH (c1:Company)-[:ADDRESS_MATCH]->(c2:Company),
(c1)<-[r1:CONTAINS_TOKEN]-(t:CompanyNameToken)-[r2:CONTAINS_TOKEN]->(c2)
RETURN c1, c2 LIMIT 100
但是,当使用
LIMIT 300
而不是 LIMIT 100
执行相同的查询时,执行计划涉及多个分支(c1
、c2
上的笛卡尔积)并且在更长的 12000 毫秒内突然有 3800 万次 DB 命中。 (过渡介于 200 和 300 之间)。
PROFILE MATCH (c1:Company)-[:ADDRESS_MATCH]->(c2:Company),
(c1)<-[r1:CONTAINS_TOKEN]-(t:CompanyNameToken)-[r2:CONTAINS_TOKEN]->(c2)
return c1, c2 limit 300
使用
exists
关键字重新表述上述查询不会显示此行为。执行计划保持不变,独立于限制,甚至没有限制。对于 LIMIT 300
,它仅在 190 毫秒内就有 600.000 次 DB 命中。
PROFILE MATCH (c1:Company)-[:ADDRESS_MATCH]->(c2:Company)
WHERE EXISTS {(c1)<-[r1:CONTAINS_TOKEN]-(t:CompanyNameToken)-[r2:CONTAINS_TOKEN]->(c2)}
return c1, c2 limit 300
三个问题:
LIMIT 100
的执行计划是应该的,避免了不必要的笛卡尔积。 LIMIT 300
的计划天真得无可救药。有没有办法强制 neo4j 执行第一个执行计划,也许有一些计划提示?EXISTS
的变体时执行计划不同?我尝试了各种规划器提示和组合,但都没有成功。令人困惑的是,几天前,这种行为并没有出现,数据几乎相同。我读到执行计划取决于数据库统计信息。但是,像
CALL db.clearQueryCaches()
或 CALL db.prepareForReplanning()
这样的命令也没有改变行为。
关于EXISTS函数,一旦找到一个匹配项,它会立即返回
true
。因此,与查找并保存所有匹配项相比,它可以更快,占用内存更少。
由于您的用例不需要使用 all
CompanyNameToken
子路径,因此只使用 EXISTS
版本的查询是有意义的。