explain作用
在通过mongo语句进行查询时,如果查询速度过慢,可以用此方法 显示 mongo在查询过程中的查询计划,是否使用索引,以及 查询,并返回了多少行 等等信息 帮助开发人员 在进一步优化之前,提供准确的信息;
explain方法参数
verbosity参数,值如下(其实就是返回值 显示信息多少的问题)
- queryPlanner 或 0/false 默认值; 返回值只返回 queryPlanner和serverInfo
- executionStats 返回值返回 详细的执行状态相关信息,如 扫描了多少行,耗时多久等等;
- allPlansExecution 或 1(非0的值都行) /true 返回值在 executionStats 字典中多了allPlansExecution字段;
explain方法返回值
查询语句如下:
db.collection_name.find({index_key_name:1}).explain(1)
返回示例如下:
{
"queryPlanner": { # 查询计划
"plannerVersion": NumberInt("1"), # 计划版本
"namespace": "db_name.collection_name", # 命名空间,作用于哪个库的哪个集合
"indexFilterSet": false, # 是否对查询使用索引过滤
"parsedQuery": { # 解析查询条件
"index_key_name": {
"$eq": 1
}
},
"queryHash": "6ACB91B3", # 仅对查询条件进行hash的16进制字符串,帮助识别相同查询条件的 其他查询操作或写操作
"planCacheKey": "ACADC259", # 和查询关联的计划缓存的hash键
"winningPlan": { # 查询优化器 选择的最优执行计划,此计划包含多个 树状结构的子阶段(一个查询计划需要多个阶段来完成);
"stage": "FETCH", # 父阶段;查询方式
"inputStage": { # 子阶段
"stage": "IXSCAN", # 子阶段的查询方式
"keyPattern": { # 索引模式
"index_key_name": 1
},
"indexName": "index_key_name_1", # 索引名称
"isMultiKey": false, # 是否是复合索引
"multiKeyPaths": { # 复合索引路径
"index_key_name": []
},
"isUnique": true, # 是否是唯一索引
"isSparse": false, # 是否是稀疏索引
"isPartial": false, # 是否是部分索引
"indexVersion": NumberInt("2"), # 索引版本
"direction": "forward", # 索引方向
"indexBounds": { # 索引查询的范围边界
"index_key_name": [ # 创建索引的key
"[1.0, 1.0]" # 边界范围
]
}
}
},
"rejectedPlans": [] # 查询又花钱 拒绝的执行计划
},
"executionStats": { # 详细的执行统计信息
"executionSuccess": true, # 是否执行成功
"nReturned": NumberInt("0"), # 符合查询条件的文档个数
"executionTimeMillis": NumberInt("0"), # 选择某个查询计划和执行查询 所耗费的总时间(毫秒)
"totalKeysExamined": NumberInt("0"), # 扫描的索引总行数
"totalDocsExamined": NumberInt("0"), # 扫描的文档总次数(即使同一个文档如果被扫描2次,则此值为2),常见于 stage为 COLLSCAN/FETCH
"executionStages": { # 用树状形式 描述 详细的执行计划
"stage": "FETCH", # 查询方式
"nReturned": NumberInt("0"),
"executionTimeMillisEstimate": NumberInt("0"), # 估计执行时间(毫秒)
"works": NumberInt("1"), # 指定查询执行阶段执行的“工作单元”的数量。查询执行将其工作划分为小单元。
# “工作单元”可能包括检查单个索引键、从集合中获取单个文档、对单个文档应用投影或进行内部簿记
"advanced": NumberInt("0"), # 由这一阶段返回到它的父阶段的中间结果或高级结果的数量。
"needTime": NumberInt("0"), # 未将中间结果提前到其父阶段的工作循环数
"needYield": NumberInt("0"), # 为了让写操作执行,而让出读锁的次数
"saveState": NumberInt("0"), # 查询阶段暂停处理并保存其当前执行状态的次数,例如准备放弃其锁
"restoreState": NumberInt("0"), # 查询阶段恢复已保存的执行状态的次数,例如,在恢复以前生成的锁之后。
"isEOF": NumberInt("1"), # 执行阶段是否已到达最后一个; 1:是 0:不是
"docsExamined": NumberInt("0"), # 扫描文档总次数
"alreadyHasObj": NumberInt("0"),
"inputStage": {
"stage": "IXSCAN",
"nReturned": NumberInt("0"),
"executionTimeMillisEstimate": NumberInt("0"),
"works": NumberInt("1"),
"advanced": NumberInt("0"),
"needTime": NumberInt("0"),
"needYield": NumberInt("0"),
"saveState": NumberInt("0"),
"restoreState": NumberInt("0"),
"isEOF": NumberInt("1"),
"keyPattern": {
"index_key_name": 1
},
"indexName": "index_key_name_1",
"isMultiKey": false,
"multiKeyPaths": {
"index_key_name": []
},
"isUnique": true,
"isSparse": false,
"isPartial": false,
"indexVersion": NumberInt("2"),
"direction": "forward",
"indexBounds": {
"index_key_name": [
"[1.0, 1.0]"
]
},
"keysExamined": NumberInt("0"), # 通过索引扫描的文档总个数
"seeks": NumberInt("1"), # 为了完成索引扫描,必须将索引游标搜索到新位置的次数。
"dupsTested": NumberInt("0"),
"dupsDropped": NumberInt("0")
}
},
"allPlansExecution": [] # 在计划选择阶段,获胜计划和被拒绝计划的部分执行信息
},
"serverInfo": { # mongo服务信息
"host": "aa8f4be",
"port": NumberInt("27010"),
"version": "4.2.0",
"gitVersion": "a4b751dcf51dd249c5865812b390cfd"
},
"ok": 1
}
关键字段解析
字段名称 | 含义 |
---|---|
stage | COLLSCAN:全表扫描 IXSCAN:索引扫描 FETCH:根据索引去检索指定文档 SHARD_MERGE:将各个分片返回数据进行合并 SHARDING_FILTER: 分片过滤 SORT:表明在内存中进行了排序 LIMIT:使用limit限制返回数 SKIP:使用skip进行跳过 IDHACK:针对_id进行查询 |
nReturned | 实际返回的文档个数 |
totalKeysExamined | 扫描的索引总行数 |
totalDocsExamined | 扫描的文档总行数 |
executionTimeMillis | 执行耗费时间(毫秒) |
使用方法
- stage 查看 对集合的 查询方式,是否全表扫描还是索引扫描等等,来判断好坏;
- 根据 nReturned,totalKeysExamined,totalDocsExamined 3个字段的值 来判断 扫描集合的效率; totalDocsExamined和nReturned 越接近越好,表名 搜索效率越高,如果扫描1万行只返回1行,则表名 效率低下,可以考虑使用索引;
Mongo中执行查询语句过程
从上述 返回的explain结果可以看出,在实际一个查询语句中会生成多个查询计划,并最终筛选出最优的查询计划和 其他被拒绝的查询计划;产生这种结果的原因 在于 mongo 会先通过查询分析器,生成多个查询计划(如,查询一条数据的语句,一个查询计划 使用索引查询,另一个查询计划 使用全表查询),并让其 并行运行所有的查询计划; 然后 查询优化器 会将先返回结果的 查询计划作为真正执行的查询计划,并且 立刻终止其他的查询计划;
mongo的这种 让所有的查询计划 一起执行,谁先返回就选中谁的方式 虽然看起来并发浪费了资源,但是却保证了 每个查询语句的执行计划一定是最优计划;相比较 关系型数据库(如Mysql)的查询优化器(先计算每个查询计划的代价,然后再决定用代价最小的查询计划) 要简单很多;
其他
- 测试时的mongo版本:4.2
- 如果发现 查询条件 没有使用索引,可以 使用 hint()方法强制使用索引;
db.collection.find({age:12}).hint({age:1}) # 查询age=12,且强制使用age索引
相关链接: