首页 > 数据库 >MongoDB4.2中索引的查询计划: 提高查询效率的关键Query Plans

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

时间:2023-04-18 17:34:15浏览次数:39  
标签:缓存 MongoDB4.2 Plans queryHash db 查询 索引 quantity

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_字段_02

MongoDB4.2中索引的查询计划: 提高查询效率的关键Query Plans_bc_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_缓存_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()方法,因此请谨慎使用索引过滤器。

标签:缓存,MongoDB4.2,Plans,queryHash,db,查询,索引,quantity
From: https://blog.51cto.com/u_13753753/6194664

相关文章

  • 1 哈希类型、 2 列表类型 、3 集合类型、 4 有序集合(zset) 、5 慢查询 、6 pipeline与
    目录1哈希类型2列表类型3集合类型4有序集合(zset)5慢查询6pipeline与事务7发布订阅8Bitmap位图9HyperLogLog1哈希类型###1---hget,hset,hdelhgetkeyfield#获取hashkey对应的field的value时间复杂度为o(1)hsetkeyfieldvalue#设置hashkey对应的field的value......
  • redis高级-day2——redis哈希类型、redis列表类型、redis集合类型、redis有序集合类型
    目录一、哈希类型二、列表类型三、集合类型四、有序集合(zset)五、慢查询六、pipeline与事务七、发布订阅八、Bitmap位图九、HyperLogLog十、作业1、http协议详情,http协议版本,http一些请求头2、如何实现服务器给客户端发送消息,websocket是什么?用过吗3、悲观锁和乐观锁,如何实现一、......
  • sql查询字段个数
    看具体是什么数据库,以oracle,mysql,sqlserver分别回答。1、oracle:select count(*) from user_tab_cols where table_name='表名';--表名含英文的话应为英文大写字母结果如图:2、mysql:select count(*) from information_schema.COLUMNS where table_name='表名';--表......
  • 查询sqlserver列名,行数及表说明
    查询sqlserver表名,行数及表说明SELECTD.NAMETABLE_NAME,B.ROWSTABLE_ROWS,F.VALUETABLE_COMMENTFROMSYSOBJECTSDLEFTJOINSYS.EXTENDED_PROPERTIESFOND.ID=F.MAJOR_IDANDF.MINOR_ID=0LEFTJOINSYSINDEXESBOND.ID=B.IDANDB......
  • 15天玩转redis —— 第七篇 同事的一次缓存操作引起对慢查询的认识
       上个星期同事做一个业务模块,需要将一个80M的数据存入到redis缓存中,想法总是好的,真操作的时候遇到了HSet超时,我们使用的是C#的StackExchange.Redis驱动。<redisCacheClientallowAdmin="true"ssl="false"connectTimeout="5000"abortConnect="false"database="......
  • 如何查询经纬度--地理盲的挣扎
    文档说明:仅仅是一个记录而已;2023-04-18缘由:地理盲鄂A车牌,第一反应是鄂尔多斯(丢人)海事局发布坐标(36-02.70N120-16.87E、36-02.20N120-17.00E),看着一脸懵,最基本的大概方向和位置都判断不出来知识陷阱笔记鄂尔多斯在内蒙古鄂湖北的简称WGS84是地球坐标系,不......
  • Oracle将想要查询的时间点集合作为一个新表,去另一个表里只查询规定时间点的数据
    最近遇到一个新需求,我们需要在一个表中选取特定时间段内一些固定时间点的数据(比如只想要取每个小时零点的数据),废物的我想不出来,让大佬教的,记录一下。假如这个表长这样,名字就叫Table吧,需要取到每个小时零点的高度 TIMEHEIGHT2023-04-1800:00:0012023-04-1801:00......
  • MyBatis查询功能
    如果查询出的数据只有一条,可以通过实体类对象接收List集合接收Map集合接收,结果如果查询出的数据有多条,一定不能用实体类对象接收,会抛异常TooManyResultsException,可以通过实体类类型的LIst集合接收Map类型的LIst集合接收在mapper接口的方法上添加@MapKey注解查询......
  • 查询练习数据
    如题。xscj(学生成绩)数据库的三张表。xs(学生)表kc(课程)表xs_kc(选课)表建议:给选课表一个流水号。......
  • 科技政策查询系统优化 (排序与隐藏文字显示的)
    1.解决了排版乱,实现了按照时间的排序2.把操作按钮去除,实现了点击政策名称直接查看文件内容的方式(运用超链接的方式)3.实现了鼠标悬停在政策名称上面显示出所有信息的方式。 ......