查询计划
对于任何给定的查询,在给定可用索引的情况下,MongoDB 查询规划器会选择并缓存最高效的查询计划。为了评估查询计划的效率,查询规划器会在试用期内运行所有候选计划。一般情况下,获胜计划是在试用期间产生最多结果同时执行最少工作量的查询计划。
关联的计划缓存条目用于具有相同计划缓存查询结构的后续查询。
下图演示了查询规划器逻辑:
注意
使用 explain
会忽略所有现有的计划缓存条目,并阻止 MongoDB 查询计划器创建新的计划缓存条目。
规划缓存条目状态
每个计划缓存查询结构都与缓存中的三种状态之一相关联:
州 | 说明 |
---|---|
缓存中不存在此形状的条目。 对于查询,如果计划缓存查询结构的缓存条目状态为 缺失: | |
缓存中的条目是此形状的占位符条目。也就是说,规划器已查看形状,计算了一个值来量化计划所需的工作量,并存储了形状占位符条目,但计划缓存查询结构不用于生成查询计划。 | |
缓存中的条目是面向获胜计划的。规划器可以使用此条目生成查询计划。 活动条目用于生成查询计划。 |
查询计划和缓存信息
要查看给定查询的查询计划信息,可以使用 db.collection.explain()
或 cursor.explain()
。
要查看集合的计划缓存信息,可以使用 $planCacheStats
聚合阶段。
计划缓存刷新
如果 mongod
重新启动或关闭,查询计划缓存将不复存在。此外:
目录操作(如索引或集合删除)会清除计划缓存。
最近最少使用 (LRU) 缓存替换机制会清除最近最少访问的缓存条目,而无论条目处于何种状态。
此外,用户还可:
使用
PlanCache.clear()
方法手动清除整个计划缓存。使用
PlanCache.clearPlansByQuery()
方法手动清除特定计划缓存条目。
计划缓存调试信息大小限制
从 MongoDB 5.0开始,仅当所有集合的plan caches
累积大小低于0.5时,计划缓存才会保存完整的plan cache
条目 GB。 当所有集合的plan caches
累积大小超过此阈值时,将存储额外的plan cache
条目,但不会包含以下调试信息:
plan cache
条目的估计大小(以字节为单位)可在 $planCacheStats
的输出中找到。
planCacheShapeHash 和 planCacheKey
planCacheShapeHash
为了帮助识别具有相同计划缓存查询结构的慢速查询,每个计划缓存查询结构都与一个查询哈希相关联。 计划缓存查询结构哈希是一个十六进制string ,表示查询结构的哈希值,并且仅依赖于查询结构。
注意
与任何哈希函数一样,两个不同的查询结构可能会产生相同的哈希值。但是,不同查询结构之间不太可能发生哈希冲突。
从 MongoDB 8.0 开始,预先存在的 queryHash
字段被重命名为 planCacheShapeHash
。如果正在使用早期版本的 MongoDB,您将看到 queryHash
而不是 planCacheShapeHash
。
planCacheKey
为了更深入地了解查询计划缓存,MongoDB 推出了 planCacheKey
。
planCacheKey
与此查询关联的计划缓存条目的键的哈希值。
注意
与 planCacheShapeHash
不同,planCacheKey
是查询结构和该结构当前可用索引的函数。换言之,如果添加/删除可以支持该查询结构的索引,则 planCacheKey
值可能会更改,而 planCacheShapeHash
值不会更改。
从 MongoDB 8.0 开始,预先存在的 queryHash
字段被重命名为 planCacheShapeHash
。如果正在使用早期版本的 MongoDB,您将看到 queryHash
而不是 planCacheShapeHash
。
例如,考虑一个具有以下索引的集合 foo
:
db.foo.createIndex( { x: 1 } ) db.foo.createIndex( { x: 1, y: 1 } ) db.foo.createIndex( { x: 1, z: 1 }, { partialFilterExpression: { x: { $gt: 10 } } } )
对集合的以下查询具有相同的结构:
db.foo.explain().find( { x: { $gt: 5 } } ) // Query Operation 1 db.foo.explain().find( { x: { $gt: 20 } } ) // Query Operation 2
给定这些查询,带有部分筛选器表达式的索引可以支持查询操作 2,但不支持查询操作 1。由于可用于支持查询操作 1 的索引与查询操作 2 的索引不同,因此,这两个查询具有不同的 planCacheKey
。
如果删除其中一个索引,或者,添加了新索引 { x: 1, a: 1
}
,则两个查询操作的 planCacheKey
将发生变化。
可用性
planCacheShapeHash
和 planCacheKey
可用于:
explain() 输出 字段:
从 MongoDB 8.0 开始,预先存在的
queryHash
字段被重命名为planCacheShapeHash
。如果正在使用早期版本的 MongoDB,您将看到queryHash
而不是planCacheShapeHash
。记录慢速查询时的日志消息,包括分析器日志消息和诊断日志消息(即 mongod/mongos 日志消息)。
$planCacheStats
聚合阶段PlanCache.listQueryShapes()
方法/planCacheListQueryShapes
命令PlanCache.getPlansByQuery()
方法/planCacheListPlans
命令
索引筛选器
索引过滤器是通过使用 planCacheSetFilter
命令设置的,并确定计划器针对查询结构评估哪些索引。计划缓存查询结构由查询、排序和投影规范的组合构成。如果给定查询结构存在索引过滤器,则计划器仅考虑过滤器中指定的索引。
当计划缓存查询结构存在索引过滤器时,MongoDB 会忽略 hint()
。要查看 MongoDB 是否为查询结构应用了索引筛选器,请检查 db.collection.explain()
或 cursor.explain()
方法的 indexFilterSet
字段。
索引过滤器仅影响规划器评估哪些索引;规划器仍可选择集合扫描作为给定计划缓存查询结构的获胜计划。
索引筛选器在服务器进程期间存在,而在关闭后会消失。MongoDB 还提供用于手动删除筛选器的命令。
由于索引筛选器会覆盖计划程序的预期行为以及 hint()
方法,因此请谨慎使用索引筛选器。
从 MongoDB 6.0 开始,索引筛选器会使用之前使用 planCacheSetFilter
命令设置的排序规则。
从 MongoDB 8.0 开始,使用查询设置而不是添加索引过滤器。从 MongoDB 8.0 开始,已弃用索引过滤器。
查询设置的功能比索引筛选器更多。 此外,索引筛选器不是持久性的,您无法轻松地为所有集群节点创建索引筛选器。 要添加查询设置并探索示例,请参阅setQuerySettings
。