了解查询计划缓存
当第一次提交 Cypher 语句时,Neo4j 将尝试确定查询是否在计划缓存中,然后再对其进行规划。默认情况下,Neo4j 将根据 dbms.query_cache_size 的 conf/neo4j.conf
参数,在缓存中保留 1000 个查询计划。实际上,这实际上代表了 2 个查询计划缓存。
-
字符串缓存
当 Cypher 最初被提交时,Cypher 语句将对字符串本身进行哈希计算。使用此生成的哈希值,我们将尝试确定语句是否已存在于计划缓存中,如果存在,则可能不需要重新规划。
但是请注意,在逻辑上相同但在大小写上不同的语句将生成不同的哈希值。以下 2 个语句虽然在语义上等效,但会生成不同的哈希值,可能需要重新规划。
match (n) return count(n);
MATCH (n) return COUNT(n);
此外,在逻辑上相同但在空格/回车符上不同的语句将生成不同的哈希值。以下 3 个语句将生成不同的哈希值,可能需要重新规划
MATCH (n) return COUNT(n);
MATCH (n) return COUNT(n);
MATCH (n)
return COUNT(n);
以 PROFILE/EXPLAIN
为前缀的 Cypher 语句将在语句被哈希之前将其 PROFILE/EXPLAIN
删除。以下 2 个语句将哈希到相同的值
MATCH (n) return COUNT(n);
PROFILE MATCH (n) return COUNT(n);
如果 Cypher 语句的哈希值在第一个缓存中找不到,Neo4j 将尝试确定它是否在第二个缓存中。
-
AST 缓存
Neo4j 编译器将查询从字符串解析为抽象语法树 (AST),它是一个查询的 object 表示形式。优化器然后对语句进行规范化,以使规划更容易。例如
match (n:Person {id:101}) return n;
将规范化为
match (n:Person) where n.id={param1} return n; {param1: 101}
在这种情况下,Neo4j 已将谓词 {id:101}
从 MATCH
模式移动到 WHERE
子句,并将 101
值参数化为参数,例如 n.id={param1}
。参数的使用在 这里 有更详细的说明
AST 不存储空格和关键字的大小写等信息,并且由于它已参数化,因此字面值可以更改,但仍然生成相同的 AST。
第二个查询缓存以这个规范化的 AST 为键。即这些查询将重用相同的查询计划。
match (n:Person) where n.id=101 return n;
match (n:Person {id:101}) return n;
MATCH ( n:Person { id : 101 } )
RETURN n;
最后,如果 Cypher 语句在第一个或第二个缓存中被找到,查询仍然可能根据 cypher.min_replan_interval 和 cypher.statistics_divergence_threshold 的 conf/neo4j.conf
参数进行重新规划。
cypher.min_replan_interval 用于定义缓存计划在重新规划之前存在的持续时间,默认为 10 秒。
cypher.statistics_divergence_threshold 用于指示 Cypher 使用的基础数据的统计信息发生了多少百分比的变化。默认值为 0.75,表示如果对象中的统计信息自上次生成缓存计划以来发生了超过 75% 的变化,则需要生成新的计划。例如,运行
// remove all :Person nodes
match (n:Person) detach delete n;
// create 10 :Person nodes
foreach (x in range (1,10) | create (n:Person {id:x}));
// list the 10 :Person nodes created
match (n:Person) return n.id order by n.id desc;
// create 8 new :Person nodes
foreach (x in range (11,18) | create (n:Person {id:x}));
// list the 18 :Person nodes
match (n:Person) return n.id order by n.id desc;
第二个 match (n:Person) return n.id order by n.id desc;
将分别被规划,特别是第二个实例,尽管具有相同的哈希值,但 :Person 的统计信息已从 10 个节点更改为 18 个节点,从而超过了 75% 的变化。
如果由于上述 2 个参数导致现有计划需要重新规划,则 logs/debug.log
将记录
2017-03-31 19:14:27.820+0000 INFO [o.n.c.i.ExecutionEngine] Discarded stale query from the query cache: match (n:Person) return n.id order by n.id desc; 2017-03-31 19:14:27.821+0000 INFO [o.n.c.i.EnterpriseCompatibilityFactory] Discarded stale query from the query cache: match (n:Person) return n.id order by n.id desc;
此外,应该注意的是,当从缓存中删除查询计划以腾出空间以供新计划使用时,会使用最不经常使用 (LFU) 算法。因此,如果添加到计划缓存中的第一个查询每 1 秒运行一次,而添加到查询计划缓存中的第二个查询每 2 分钟添加一次,那么当我们需要从缓存中删除查询计划以腾出空间用于新查询时,我们将删除第二个查询而不是第一个,因为第一个更常被调用。
最后应该注意的是,任何模式更改(例如索引/约束创建/删除)都会刷新整个查询计划缓存。
此页面是否有帮助?