Mongodb聚合:$planCacheStats
执行查询时,MongoDB 查询规划器会根据可用索引选择并缓存效率最高的查询计划。$planCache
可以返回所有集合的查询计划缓存信息。要使用$planCache
,必须把$planCacheStats
阶段放在管道最前面。
语法
{ $planCacheStats: { } }
使用
$planCacheStats
必须是管道的第一个阶段$planCacheStats
不能应用于事务和$facet
阶段$planCacheStats
需要的读关注级别为"local"- 如果系统启用了权限控制,用户必须要有对集合的
planCacheRead
授权 - 当使用查询加密,
$planCacheStats
阶段会忽略对加密集合的操作,即便是操作被正常缓存。
读取偏好
$planCacheStats
在选择返回计划缓存信息的主机时,会遵守读取偏好。
应用程序可能以副本集的不同成员为目标。因此,每个副本集成员可能会收到不同的读取命令,并拥有与其他成员不同的计划缓存信息。尽管如此,在副本集或分片集群上运行$planCacheStats
仍会遵守正常的读取优先规则。也就是说,在副本集上,该操作只从副本集的一个成员处收集计划缓存信息;在分片集群上,该操作只从每个分片副本集的一个成员处收集计划缓存信息。
输出
$planCacheStats
的输出取决于完成查询所使用的查询引擎,version
字段的值表明使用了哪个查询引擎:
1
表示使用了经典引擎。2
表示使用了基于时隙的查询执行引擎。
经典执行引擎
对于使用经典执行引擎的查询,$planCacheStats
会返回与下面类似的文档:
{
"version" : 1,
"createdFromQuery" : <document>,
"queryHash" : <hexadecimal string>,
"planCacheKey" : <hexadecimal string>,
"isActive" : <boolean>,
"works" : <NumberLong>,
"cachedPlan" : {
"stage" : <STAGE1>,
"filter" : <document>,
"inputStage" : {
"stage" : <STAGE2>,
...
}
},
"timeOfCreation" : <date>,
"creationExecStats" : [ //每个候选计划的执行统计文档
{
"nReturned" : <num>,
"executionTimeMillisEstimate" : <num>,
"totalKeysExamined" : <num>,
"totalDocsExamined" :<num>,
"executionStages" : {
"stage" : <STAGE A>,
...
"inputStage" : {
"stage" : <STAGE B>,
...
}
}
},
...
],
"candidatePlanScores" : [
<number>,
...
],
"indexFilterSet" : <boolean>,
"estimatedSizeBytes" : <num>,
"host" : <string>,
"shard" : <string>
}
对于使用基于slot的查询执行引擎的查询,$planCacheStats
会返回与下面类似的文档:
{
"version" : 2,
"queryHash" : <hexadecimal string>,
"planCacheKey" : <hexadecimal string>,
"isActive" : <boolean>,
"works" : <NumberLong>,
"cachedPlan" : {
"slots" : <string>,
"stages": <string>
},
"indexFilterSet" : <boolean>,
"estimatedSizeBytes" : <num>,
"host" : <string>
}
每份文档都包含各种查询计划和执行统计信息,包括:
字段 | 说明 |
---|---|
version | 查询引擎的版本号,1 为经典引擎,2为基于slot的执行引擎 |
createdFromQuery | 一个包含了产生该缓存条目的特定查询的文档 |
isActive | 布尔值,表示是否处于活动状态,如果为true,表示查询规划器当前正在使用它产生查询计划;如果为false,则表示查询规划期当前没有使用它 |
queryHash | 一个16进制格式的字符串,就是查询模型的哈希值 |
works | 查询规划器评估候选计划的试运行期间查询执行计划执行的 "工作单元 "数量 |
timeOfCreation | 创建条目的时间 |
creationExecStats | 执行统计文档数组。该数组包含每个候选计划的文档 |
candidatePlanScores | creationExecStats 数组中列出的候选计划的得分数组 |
indexFilterSet | 布尔值,表示查询形状是否存在索引过滤器 |
estimatedSizeBytes | 计划缓存条目的估计大小(字节) |
host | mongod实例的主机名和端口号。当运行于分片集群时,操作返回每个分片副本集的信息,并使用分片和主机字段进行区分 |
shard | 分片名称,仅在分片集群上运行时可用 |
举例
下面的例子使用orders
集合:
db.orders.insertMany( [
{ "_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( { quantity: 1 } );
db.orders.createIndex( { quantity: 1, type: 1 } );
db.orders.createIndex(
{ item: 1, price: 1 },
{ partialFilterExpression: { price: { $gte: NumberDecimal("10")} } }
);
其中,索引{ item:1, price: 1 }
是部分索引,仅索引价格字段大于或等于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" } )
上面的查询是通过基于slot的查询执行引擎完成的。
返回查询缓存中所有条目的信息
下面的聚合管道使用$planCacheStats
返回集合的计划缓存条目信息:
db.orders.aggregate( [
{ $planCacheStats: { } }
] )
输出:
[
{
version: '2',
queryHash: '478AD696',
planCacheKey: '21AE23AD',
isActive: true,
works: Long("7"),
timeOfCreation: ISODate("2023-05-22T20:33:49.031Z"),
cachedPlan: {
...
},
indexFilterSet: false,
isPinned: false,
estimatedSizeBytes: Long("8194"),
host: 'mongodb1.example.net:27018'
},
{
version: '2',
queryHash: '3D8AFDC6',
planCacheKey: '1C2C4360',
isActive: true,
works: Long("6"),
timeOfCreation: ISODate("2023-05-22T20:33:50.584Z"),
cachedPlan: {
...
},
indexFilterSet: false,
isPinned: false,
estimatedSizeBytes: Long("11547"),
host: 'mongodb1.example.net:27018'
},
{
version: '2',
queryHash: '27285F9B',
planCacheKey: '20BB9404',
isActive: true,
works: Long("1"),
timeOfCreation: ISODate("2023-05-22T20:33:49.051Z"),
cachedPlan: {
...
},
indexFilterSet: false,
isPinned: false,
estimatedSizeBytes: Long("7406"),
host: 'mongodb1.example.net:27018'
},
{
version: '2',
queryHash: '478AD696',
planCacheKey: 'B1435201',
isActive: true,
works: Long("5"),
timeOfCreation: ISODate("2023-05-22T20:33:49.009Z"),
cachedPlan: {
...
},
indexFilterSet: false,
isPinned: false,
estimatedSizeBytes: Long("7415"),
host: 'mongodb1.example.net:27018'
}
],
查找查询哈希缓存详情
要返回特定查询散列的计划缓存信息,可使用$planCacheStats
阶段后,可以在planCacheKey
字段上添加$match
。
以下聚合管道使用$planCacheStats
之后的$match
阶段来返回特定查询散列的特定信息:
db.orders.aggregate( [
{ $planCacheStats: { } },
{ $match: { planCacheKey: "B1435201"} }
] )
输出:
[
{
version: '2',
queryHash: '478AD696',
planCacheKey: 'B1435201',
isActive: true,
works: Long("5"),
timeOfCreation: ISODate("2023-05-22T20:33:49.009Z"),
cachedPlan: {
slots: '$$RESULT=s11 env: { s3 = 1684787629009 (NOW), s6 = Nothing, s5 = Nothing, s1 = TimeZoneDatabase(Asia/Kuwait...Etc/UCT) (timeZoneDB), s10 = {"item" : 1, "price" : 1}, s2 = Nothing (SEARCH_META) }',
stages: '[2] nlj inner [] [s4, s7, s8, s9, s10] \n' +
' left \n' +
' [1] cfilter {(exists(s5) && exists(s6))} \n' +
' [1] ixseek s5 s6 s9 s4 s7 s8 [] @"358822b7-c129-47b7-ad7f-40017a51b03c" @"item_1_price_1" true \n' +
' right \n' +
' [2] limit 1 \n' +
' [2] seek s4 s11 s12 s7 s8 s9 s10 none none [] @"358822b7-c129-47b7-ad7f-40017a51b03c" true false \n'
},
indexFilterSet: false,
isPinned: false,
estimatedSizeBytes: Long("7415"),
host: 'mongodb1.example.net:27018'
}
]