Mongo explain()使用

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索引

相关链接:

cursor.explain() — MongoDB Manualhttps://docs.mongodb.com/manual/reference/method/cursor.explain/#mongodb-method-cursor.explain

https://docs.mongodb.com/v4.2/reference/explain-results/index.htmlicon-default.png?t=L892https://docs.mongodb.com/v4.2/reference/explain-results/index.html 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值