查询计划
对于任何给定的查询,MongoDB查询规划器会选择并缓存最有效的查询计划,前提是有可用的索引。为了评估查询计划的效率,查询规划器会在试验期间运行所有候选计划。一般来说,获胜的计划是在试验期间产生最多结果同时完成最少工作的查询计划。
关联的计划缓存条目用于后续具有相同计划缓存查询形状的查询.
以下图表说明了查询规划器逻辑
注意
使用explain
忽略所有现有的计划缓存条目,并阻止MongoDB查询规划器创建新的计划缓存条目。
计划缓存条目状态
每个计划缓存查询形状都与缓存中的三种状态之一相关联
状态 | 描述 |
---|---|
缓存中的条目是此形状的占位符条目。也就是说,规划器已经看到了该形状,计算了一个量化计划所需工作量的值,并存储了形状占位符条目,但计划缓存查询形状 不 用于生成查询计划。 对于查询,如果形状的缓存条目状态为 不活跃: | |
有关触发计划缓存更改的其他场景,请参阅计划缓存刷新。
查询计划和缓存信息
要查看特定查询的查询计划信息,可以使用db.collection.explain()
或cursor.explain()
。
要查看集合的计划缓存信息,可以使用$planCacheStats
聚合阶段。
计划缓存刷新
如果mongod
重启或关闭,则查询计划缓存不会持久化。此外
目录操作,如索引或集合删除,会清除计划缓存。
最近最少使用(LRU)缓存替换机制会清除最少被访问的缓存条目,不论其状态。
用户还可以
使用
PlanCache.clear()
方法手动清除整个计划缓存。使用
PlanCache.clearPlansByQuery()
方法手动清除特定的计划缓存条目。
计划缓存调试信息大小限制
从 MongoDB 5.0 开始,如果所有集合的计划缓存累积大小低于 0.5 GB,则会保存完整的 计划缓存 条目。当所有集合的计划缓存累积大小超过此阈值时,额外的计划缓存条目将存储,但不包含以下调试信息
计划缓存条目的估计大小(以字节为单位)可在 $planCacheStats
的输出中找到。
planCacheShapeHash 和 planCacheKey
planCacheShapeHash
为了帮助识别具有相同查询缓存形状的慢查询,每个查询缓存形状都与一个查询哈希值相关联。查询缓存形状哈希是一个十六进制字符串,表示查询形状的哈希值,仅取决于查询形状。
注意
与任何哈希函数一样,两个不同的查询形状可能会导致相同的哈希值。但是,不同查询形状之间发生哈希碰撞的概率很低。
从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
可在以下位置找到:
从MongoDB 8.0版本开始,原有的
queryHash
字段重命名为planCacheShapeHash
。如果您使用的是早期版本的MongoDB,您将看到queryHash
而不是planCacheShapeHash
。分析器日志消息 和 诊断日志消息(即 mongod/mongos 日志消息),当记录慢查询时。
$planCacheStats
聚合阶段PlanCache.listQueryShapes()
方法/planCacheListQueryShapes
命令PlanCache.getPlansByQuery()
方法/planCacheListPlans
命令
索引过滤器
使用 planCacheSetFilter
命令设置索引过滤器,以确定规划器为 查询形状 评估哪些索引。计划缓存查询形状由查询、排序和投影规范的组合组成。如果给定查询形状存在索引过滤器,则规划器仅考虑过滤器中指定的索引。
当查询缓存查询形状存在索引过滤器时,MongoDB会忽略hint()
。要检查MongoDB是否为一个查询形状应用了索引过滤器,请检查indexFilterSet
字段,这个字段可以在db.collection.explain()
或cursor.explain()
方法中找到。
索引过滤器仅影响查询优化器评估哪些索引;对于给定的查询缓存查询形状,优化器仍然可能选择集合扫描作为最佳计划。
索引过滤器在服务器进程期间存在,并且在关闭后不会持久化。MongoDB还提供了一个命令来手动移除过滤器。
由于索引过滤器会覆盖查询优化器预期的行为以及hint()
方法的行为,因此请谨慎使用索引过滤器。
从MongoDB 6.0开始,索引过滤器使用排序规则,这是使用planCacheSetFilter
命令之前设置的。
从MongoDB 8.0开始,使用查询设置而不是添加索引过滤器。从MongoDB 8.0开始,索引过滤器已被弃用。
查询设置的功能比索引过滤器更多。此外,索引过滤器是不可持久的,并且您无法轻松地为所有集群节点创建索引过滤器。要添加查询设置并查看示例,请参阅setQuerySettings
。