MongoDB4.2中索引的查询计划: 提高查询效率的关键Query Plans
  nQkVcpdWfLDr 2023年11月02日 42 0

MongoDB查询优化器处理查询并为给定可用索引的查询选择最有效的查询计划。 然后,查询系统在每次运行查询时都使用此查询计划。

查询优化器仅缓存那些可以有多个可行计划的查询形状的计划。

对于每个查询,查询计划程序在查询计划缓存中搜索适合查询形状的条目。 如果没有匹配的条目,则查询计划程序会生成候选计划,以便在试用期内进行评估。 查询计划程序选择获胜计划,创建包含获胜计划的缓存条目,并使用它来生成结果文档。

如果存在匹配条目,则查询计划程序将根据该条目生成计划,并通过replanning机制评估其性能。 此机制根据计划性能进行pass/fail决策,并保留或逐出缓存条目。 在逐出时,查询计划程序使用常规计划过程选择新计划并对其进行缓存。 查询计划程序执行计划并返回查询的结果文档。

下图说明了查询计划程序逻辑:

MongoDB4.2中索引的查询计划: 提高查询效率的关键Query Plans_bc

有关触发计划缓存更改的其他方案,请参阅规划缓存刷新 。

1.查询计划和缓存信息

要查看给定查询的查询计划信息,可以使用db.collection.explain()或cursor.explain() 。

从MongoDB 4.2开始,您可以使用$planCacheStats聚合阶段查看集合的计划缓存信息。

(1).$planCacheStats

版本4.2中的新功能,返回集合的计划缓存信息。 该阶段为每个计划缓存条目返回一个文档。

$planCacheStats阶段必须是管道中的第一个阶段。 该阶段将空文档作为参数,并具有以下语法:

{ $planCacheStats: { } }

注意:$planCacheStats聚合阶段优先于以下方法和命令,这些方法和命令已在4.2中弃用:

PlanCache.getPlansByQuery()方法/ planCacheListPlans命令,和  PlanCache.listQueryShapes()方法/ planCacheListQueryShapes命令。

注意事项

管道:$planCacheStats必须是聚合管道中的第一个阶段。

限制:

a.$planCacheStats不允许在以下阶段使用:
  a1.transactions
  a2.$facet聚合阶段
b.$planCacheStats需要read concern级别local 。
c.$planCacheStats不能在mongos实例上运行。

访问控制:

在使用authorization运行的系统上,用户必须具有该集合的planCacheRead特权。

输出:

对于每个计划缓存条目, $planCacheStats阶段返回类似于以下内容的文档:

{
   "createdFromQuery" : <document>,
   "queryHash" : <hexadecimal string>,
   "planCacheKey" : <hexadecimal string>,
   "isActive" :  <boolean>,
   "works" : <NumberLong>,
   "cachedPlan" : {
      "stage" : <STAGE1>,
      "filter" : <document>,
      "inputStage" : {
         "stage" : <STAGE2>,
         ...
      }
   },
   "timeOfCreation" : <date>,
   "creationExecStats" : [   // Exec Stats Document for each candidate plan
      {
         "nReturned" : <num>,
         "executionTimeMillisEstimate" : <num>
         "totalKeysExamined" : <num>
         "totalDocsExamined" :<num>
         "executionStages" : {
            "stage" : <STAGE A>,
            ...
            "inputStage" : {
               "stage" : <STAGE B>,
               ...
            }
         }
      },
      ...
   ],
   "candidatePlanScores" : [
      <number>,
      ...
   ],
   "indexFilterSet" : <boolean>
}

每个文档都包含各种查询计划和执行统计信息,包括:

MongoDB4.2中索引的查询计划: 提高查询效率的关键Query Plans_bc_02

MongoDB4.2中索引的查询计划: 提高查询效率的关键Query Plans_字段_03

本节中的示例使用以下orders集合:

db.orders.insert([
   { "_id" : 1, "item" : "abc", "price" : NumberDecimal("12"), "quantity" : 2, "type": "apparel" },
   { "_id" : 2, "item" : "jkl", "price" : NumberDecimal("20"), "quantity" : 1, "type": "electronics" },
   { "_id" : 3, "item" : "abc", "price" : NumberDecimal("10"), "quantity" : 5, "type": "apparel" },
   { "_id" : 4, "item" : "abc", "price" : NumberDecimal("8"), "quantity" : 10, "type": "apparel" },
   { "_id" : 5, "item" : "jkl", "price" : NumberDecimal("15"), "quantity" : 15, "type": "electronics" }
])

在集合上创建以下索引:

db.orders.createIndex( { item: 1 } );
db.orders.createIndex( { item: 1, quantity: 1 } );
db.orders.createIndex( { item: 1, price: 1 }, { partialFilterExpression: { price: { $gte: NumberDecimal("10")} } } );
db.orders.createIndex( { quantity: 1 } );
db.orders.createIndex( { quantity: 1, type: 1 } );

注意:索引{ item: 1, price: 1 }是部分索引 ,仅索引price字段大于或等于NumberDecimal("10")文档。

对集合运行一些查询:

db.orders.find( { item: "abc", price: { $gte: NumberDecimal("10") } } )
db.orders.find( { item: "abc", price: { $gte: NumberDecimal("5") } } )
db.orders.find( { quantity: { $gte: 20 } } )
db.orders.find( { quantity: { $gte: 5 }, type: "apparel" } )

查询缓存中所有条目的返回信息 

以下聚合管道使用$planCacheStats返回有关集合的计划缓存条目的信息:

db.orders.aggregate( [
   { $planCacheStats: { } }
] )

该操作返回缓存中的所有条目:

{                                               // Plan Cache Entry 1
   "createdFromQuery" : {
      "query" : { "quantity" : { "$gte" : 5 }, "type" : "apparel" },
      "sort" : { },
      "projection" : { }
   },
   "queryHash" : "4D151C4C",
   "planCacheKey" : "DD67E353",
   "isActive" : true,
   "works" : NumberLong(4),
   "cachedPlan" : {
      ...
   },
   "timeOfCreation" : ISODate("2019-02-04T20:30:10.414Z"),
   "creationExecStats" : [
      {
         ... // Exec Stats for Candidate 1
      },
      {
         ... // Exec Stats for Candidate 2
      }
   ],
   "candidatePlanScores" : [
      1.5003000000000002,
      1.5003000000000002
   ],
   "indexFilterSet" : false
}
{                                               // Plan Cache Entry 2
   "createdFromQuery" : {
      "query" : { "quantity" : { "$gte" : 20 } },
      "sort" : { },
      "projection" : { }
   },
   "queryHash" : "23B19B75",
   "planCacheKey" : "6F23F858",
   "isActive" : true,
   "works" : NumberLong(1),
   "cachedPlan" : {
      ...
   },
   "timeOfCreation" : ISODate("2019-02-04T20:30:10.412Z"),
   "creationExecStats" : [
      {
         ... // Exec Stats for Candidate 1
      },
      {
         ... // Exec Stats for Candidate 2
      }
   ],
   "candidatePlanScores" : [
      1.0003000000000002,
      1.0003000000000002
   ],
   "indexFilterSet" : false
}
{                                               // Plan Cache Entry 3
   "createdFromQuery" : {
      "query" : { "item" : "abc", "price" : { "$gte" : NumberDecimal("5") } },
      "sort" : { },
      "projection" : { }
   },
   "queryHash" : "117A6B10",
   "planCacheKey" : "A1824628",
   "isActive" : true,
   "works" : NumberLong(4),
   "cachedPlan" : {
      ...
   },
   "timeOfCreation" : ISODate("2019-02-04T20:30:10.410Z"),
   "creationExecStats" : [
      {
         ... // Exec Stats for Candidate 1
      },
      {
         ... // Exec Stats for Candidate 2
      }
   ],
   "candidatePlanScores" : [
      1.7503000000000002,
      1.7503000000000002
   ],
   "indexFilterSet" : false
}
{                                               // Plan Cache Entry 4
   "createdFromQuery" : {
      "query" : { "item" : "abc", "price" : { "$gte" : NumberDecimal("10") } },
      "sort" : { },
      "projection" : { }
   },
   "queryHash" : "117A6B10",
   "planCacheKey" : "2E6E536B",
   "isActive" : true,
   "works" : NumberLong(3),
   "cachedPlan" : {
      ...
   },
   "timeOfCreation" : ISODate("2019-02-04T20:30:10.408Z"),
   "creationExecStats" : [
      {
         ... // Exec Stats for Candidate 1
      },
      {
         ... // Exec Stats for Candidate 2
      },
      {
         ... // Exec Stats for Candidate 3
      }
   ],
   "candidatePlanScores" : [
      1.6669666666666663,
      1.6669666666666665,
      1.6669666666666665
   ],
   "indexFilterSet" : false
}

列表查询形状,与planCacheListQueryShapes命令一样, planCacheStats阶段可用于获取存在缓存计划的所有查询形状的列表。

planCacheStats聚合阶段优先于已弃用的PlanCache.listQueryShapes()方法和不推荐使用的planCacheListQueryShapes命令。  

例如,以下使用$project阶段仅输出queryHash字段和queryHash字段。

db.orders.aggregate( [ { $planCacheStats: { } } , { $project: {createdFromQuery: 1, queryHash: 1 } } ] )

该操作返回以下查询形状:

{ "createdFromQuery" : { "query" : { "item" : "abc", "price" : { "$gte" : NumberDecimal("5") } }, "sort" : {  }, "projection" : {  } }, "queryHash" : "117A6B10" }
{ "createdFromQuery" : { "query" : { "quantity" : { "$gte" : 5 }, "type" : "apparel" }, "sort" : {  }, "projection" : {  } }, "queryHash" : "4D151C4C" }
{ "createdFromQuery" : { "query" : { "quantity" : { "$gte" : 20 } }, "sort" : {  }, "projection" : {  } }, "queryHash" : "23B19B75" }
{ "createdFromQuery" : { "query" : { "item" : "abc", "price" : { "$gte" : NumberDecimal("10") } }, "sort" : {  }, "projection" : {  } }, "queryHash" : "117A6B10" }

查找查询形状的缓存条目详细信息,要返回特定查询形状的计划缓存信息, planCacheStats阶段后面可以跟上planCacheKey字段上的match 。

以下聚合管道使用planCacheStats后跟match和project来返回特定查询形状的特定信息:

db.orders.aggregate( [
   { $planCacheStats: { } },
   { $match: { planCacheKey: "DD67E353"} }
] )

该操作返回以下内容:

{
   "createdFromQuery" : {
      "query" : { "quantity" : { "$gte" : 5 }, "type" : "apparel" },
      "sort" : { },
      "projection" : { }
   },
   "queryHash" : "4D151C4C",
   "planCacheKey" : "DD67E353",
   "isActive" : false,
   "works" : NumberLong(4),
   "cachedPlan" : {
      "stage" : "FETCH",
      "inputStage" : {
         "stage" : "IXSCAN",
         "keyPattern" : {
            "quantity" : 1,
            "type" : 1
         },
         "indexName" : "quantity_1_type_1",
         "isMultiKey" : false,
         "multiKeyPaths" : {
            "quantity" : [ ],
            "type" : [ ]
         },
         "isUnique" : false,
         "isSparse" : false,
         "isPartial" : false,
         "indexVersion" : 2,
         "direction" : "forward",
         "indexBounds" : {
            "quantity" : [
               "[5.0, inf.0]"
            ],
            "type" : [
               "[\"apparel\", \"apparel\"]"
            ]
         }
      }
   },
   "timeOfCreation" : ISODate("2019-07-10T21:39:04.580Z"),
   "creationExecStats" : [
      {
         "nReturned" : 2,
         "executionTimeMillisEstimate" : 0,
         "totalKeysExamined" : 3,
         "totalDocsExamined" : 2,
         "executionStages" : {
            "stage" : "FETCH",
            "nReturned" : 2,
            "executionTimeMillisEstimate" : 0,
            "works" : 4,
            "advanced" : 2,
            "needTime" : 1,
            "needYield" : 0,
            "saveState" : 0,
            "restoreState" : 0,
            "isEOF" : 1,
            "docsExamined" : 2,
            "alreadyHasObj" : 0,
            "inputStage" : {
               "stage" : "IXSCAN",
               "nReturned" : 2,
               "executionTimeMillisEstimate" : 0,
               "works" : 4,
               "advanced" : 2,
               "needTime" : 1,
               "needYield" : 0,
               "saveState" : 0,
               "restoreState" : 0,
               "isEOF" : 1,
               "keyPattern" : {
                  "quantity" : 1,
                  "type" : 1
               },
               "indexName" : "quantity_1_type_1",
               "isMultiKey" : false,
               "multiKeyPaths" : {
                  "quantity" : [ ],
                  "type" : [ ]
               },
               "isUnique" : false,
               "isSparse" : false,
               "isPartial" : false,
               "indexVersion" : 2,
               "direction" : "forward",
               "indexBounds" : {
                  "quantity" : [
                     "[5.0, inf.0]"
                  ],
                  "type" : [
                     "[\"apparel\", \"apparel\"]"
                  ]
               },
               "keysExamined" : 3,
               "seeks" : 2,
               "dupsTested" : 0,
               "dupsDropped" : 0
            }
         }
      },
      {
         "nReturned" : 2,
         "executionTimeMillisEstimate" : 0,
         "totalKeysExamined" : 3,
         "totalDocsExamined" : 3,
         "executionStages" : {
            "stage" : "FETCH",
            "filter" : {
               "type" : {
                  "$eq" : "apparel"
               }
            },
            "nReturned" : 2,
            "executionTimeMillisEstimate" : 0,
            "works" : 4,
            "advanced" : 2,
            "needTime" : 1,
            "needYield" : 0,
            "saveState" : 0,
            "restoreState" : 0,
            "isEOF" : 1,
            "docsExamined" : 3,
            "alreadyHasObj" : 0,
            "inputStage" : {
               "stage" : "IXSCAN",
               "nReturned" : 3,
               "executionTimeMillisEstimate" : 0,
               "works" : 4,
               "advanced" : 3,
               "needTime" : 0,
               "needYield" : 0,
               "saveState" : 0,
               "restoreState" : 0,
               "isEOF" : 1,
               "keyPattern" : {
                  "quantity" : 1
               },
               "indexName" : "quantity_1",
               "isMultiKey" : false,
               "multiKeyPaths" : {
                  "quantity" : [ ]
               },
               "isUnique" : false,
               "isSparse" : false,
               "isPartial" : false,
               "indexVersion" : 2,
               "direction" : "forward",
               "indexBounds" : {
                  "quantity" : [
                     "[5.0, inf.0]"
                  ]
               },
               "keysExamined" : 3,
               "seeks" : 1,
               "dupsTested" : 0,
               "dupsDropped" : 0
            }
         }
      }
   ],
   "candidatePlanScores" : [
      1.5003000000000002,
      1.5003000000000002
   ],
   "indexFilterSet" : false
}

2.计划缓存刷新

如果mongod重新启动或关闭,查询计划缓存不会保留。 此外,索引或集合等目录操作会清除计划缓存。用户还可以:

(1).使用PlanCache.clear()方法手动清除整个计划缓存。

删除集合的所有缓存查询计划。

该方法只能从特定集合的计划缓存对象中使用;即

db.collection.getPlanCache().clear()
(2).使用PlanCache.clearPlansByQuery()方法手动清除特定的计划缓存条目。  

清除指定查询形状的缓存查询计划。该方法只能从特定集合的计划缓存对象中使用;即

db.collection.getPlanCache().clearPlansByQuery( <query>, <projection>, <sort> )

plancache.clearplansbyquery()方法接受以下参数:

MongoDB4.2中索引的查询计划: 提高查询效率的关键Query Plans_bc_04

例子:如果集合orders具有以下查询形状: 

{
  "query" : { "qty" : { "$gt" : 10 } },
  "sort" : { "ord_date" : 1 },
  "projection" : { },
  "queryHash" : "9AAD95BE" // Available starting in MongoDB 4.2
}

以下操作将删除为形状缓存的查询计划:

db.orders.getPlanCache().clearPlansByQuery(
   { "qty" : { "$gt" : 10 } },
   { },
   { "ord_date" : 1 }
)

3.queryHash和planCacheKey  

(1).queryHash

为了帮助识别具有相同查询形状的慢查询,从MongoDB 4.2开始,每个查询形状都与queryHash相关联。 queryHash是十六进制字符串,表示查询形状的哈希值,仅依赖于查询形状。  

注意:与任何散列函数一样,两个不同的查询形状可能导致相同的散列值。 但是,不太可能在不同查询形状之间发生散列冲突。

(2).planCacheKey

为了更深入地了解查询计划缓存 ,MongoDB 4.2引入了planCacheKey 。planCacheKey是与查询关联的计划缓存条目的键的哈希值。  

注意:与queryHash不同, planCacheKey是查询形状和形状的当前可用索引的函数。 也就是说,如果添加/删除可支持查询形状的索引,则planCacheKey值可能会更改,而queryHash值不会更改。

例如,考虑具有以下索引的集合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都将更改。

queryHash和planCacheKey可用于:

a.explain()输出字段: queryPlanner.queryHash和queryPlanner.planCacheKey
b.记录慢查询时,探查器日志消息和诊断日志消息(即mongod / mongos日志消息) 。
c.$planCacheStats聚合阶段( MongoDB 4.2中的新增功能 )
d.PlanCache.listQueryShapes()方法/ planCacheListQueryShapes命令
e.PlanCache.getPlansByQuery()方法/ planCacheListPlans命令

4.索引过滤器(2.6版中的新功能)

索引筛选器确定优化程序为查询形状评估的索引。 查询形状由查询,排序和投影规范的组合组成。 如果给定查询形状存在索引过滤器,则优化程序仅考虑过滤器中指定的那些索引。

当查询形状存在索引过滤器时,MongoDB会忽略hint() 。 要查看MongoDB是否为查询形状应用了索引筛选器,请检查db.collection.explain()或cursor.explain()方法的indexFilterSet字段。

索引过滤器仅影响优化程序评估的索引; 优化器仍然可以选择集合扫描作为给定查询形状的获胜计划。

索引筛选器在服务器进程的持续时间内存在,并且在关闭后不会保留。 MongoDB还提供了手动删除过滤器的命令。

由于索引过滤器会覆盖优化程序的预期行为以及hint()方法,因此请谨慎使用索引过滤器。

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

推荐阅读
  lG7RE7vNF4mc   2023年11月13日   30   0   0 3d权重字段
  ckHWW4G1YMhd   2023年11月13日   24   0   0 bcoracle
  wpWn7yzs0oKF   2023年11月13日   37   0   0 数据库字段SQL
  sX9JkgY3DY86   2023年11月13日   32   0   0 bcget请求List
  WfuqbRz7OUwd   2023年11月13日   30   0   0 bc软连接java
  nQkVcpdWfLDr   2023年11月13日   34   0   0 数据2d字段
nQkVcpdWfLDr