mongoDB 3.0+ explain

原创
2018/02/07 17:22
阅读数 1K

 

1、explain 函数

3.0 前后版本的explain差别很大,因为版本既然已经升级就没必要去研究低版本的了。

explain 我们叫做查询优化器,mongo 与其他数据库比如MySQL还是有所不同,所谓不同不是结果不同,而是过程不同。那么mongo的优化器确定最优查询线路的过程是怎么样的呢?

1)如果一个索引能够精确匹配一个查询,那么就确定使用这个索引,同时缓存起来,下次直接使用

2)如果有多个索引,那么mongo 分别并行使用这些索引去检索数据,最早返回 100 个结果的就是最优的,同时缓存起来,下次直接使用

3)如果有重建索引、结果集发生变化、已经执行过1000次的查询的任何一种情况,mongo都会清空之前的缓存,重新使用 1 、2 来确定新的最优索引。

 

看着这个过程,肯定和MySQL的不一致,MySQL是每次都对SQL进行分析查找最优索引,而且他不是真正的去获取数据。

 

 

2、语法

1)db.getCollection('examClassStat').find({schoolId:145}).explain(str);

str = null 或 无参,只会把最基础的 queryPlanner 显示出来,当然有附带的 serverInfo 信息

str = queryPlanner,和第一种情况完全一致

str = executionStats,除了 queryPlanner 信息,还有 executionStats 详细信息

str = allPlansExecution,会显示 queryPlanner 、executionStats、allPlansExecution 三部分

 

 

3、注意事项

1)一般情况下直接使用无参的 explain 就 OK 了,因为简单看一下是否使用索引就OK,真正说发现很慢的时候才会一步步深入的去获取信息。

 

2)主要是 explain 返回的两个对象要分清轻重,其一就是 winningPlan.stage 这个表示最后一阶段执行的类型;其二就是 winningPlan.inputStage.stage 表示第一阶段执行的类型;那么可以这样任务先执行的信息都放在 inputStage 里,最后执行的放在 winningPlan 里。

第一阶段就是真正使用索引地方,因此 winningPlan.inputStage.stage 是关键

第二阶段是指获取文档的地方,即 winningPlan.stage , 这个状态其实意义没那么重要了。

 

3)stage 可以说是衡量查询写得好坏的最直接的字段,他有如下几个值

COLLSCAN:全表扫描

IXSCAN:索引扫描

FETCH:根据索引去检索指定document

SHARD_MERGE:将各个分片返回数据进行merge

SORT:表明在内存中进行了排序

LIMIT:使用limit限制返回数

SKIP:使用skip进行跳过

IDHACK:针对_id进行查询

SHARDING_FILTER:通过mongos对分片数据进行查询

COUNT:利用db.coll.explain().count()之类进行count运算

COUNTSCAN:count不使用Index进行count时的stage返回

COUNT_SCAN:count使用了Index进行count时的stage返回

SUBPLA:未使用到索引的$or查询的stage返回

TEXT:使用全文索引进行查询时候的stage返回

PROJECTION:限定返回字段时候stage的返回

那么,建议就是第一阶段的 stage 应该是 IXSCAN 等用到索引的类型。

 

4)除了 stage 外,还有几个你常用的字段

-- 数量上,最优的结果自然是 三者都相等了,而且值小的话就再好不过了

executionStats.nReturned:实际返回总数

executionStats.totalKeysExamined:第一阶段遍历的总数

executionStats.totalDocsExamined:第二阶段遍历的总数

 

-- 时间上,值越小说明越快

executionStats.executionTimeMillis:实际耗费总时间

executionStats.executionStages.executionTimeMillisEstimate:第二阶段耗费时长

executionStats.executionStages.inputStage.executionTimeMillisEstimate:第一阶段耗费时长

 

5)通过 explain 分两个阶段获取数据可以得知,如果数据量很少的情况下,那么还是用索引去检索数据的话反而会适得其反,因为使用索引需要先查找索引条目,然后通过索引条目找到对应的文档;但是如果你是全表扫描的话,只需要第二个阶段就能完成。

这个也只是一个特例,不管怎么样,互联网时代数据量总会变大。

另外,想要确定使用哪个索引是可以使用 hint 函数, find().hint("index_name");

 

4、字段说明

db.getCollection('examClassStat')

.find({schoolId:145},{examBaseId:1,"_id":0}).explain("allPlansExecution")

------输出如下

{
    "queryPlanner": {
        "plannerVersion": 1,
        "namespace": "develop.examClassStat",
        "indexFilterSet": false,
        "parsedQuery": { /*  解析查询语句  */
            "schoolId": {
                "$eq": 145
            }
        },
        "winningPlan": {
            "stage": "PROJECTION",   /*  最后一阶段的执行类型  */
            "transformBy": {
                "examBaseId": 1,
                "_id": 0
            },
            "inputStage": {
                "stage": "COLLSCAN",  /*  第一阶段的执行类型  */
                "filter": {
                    "schoolId": {
                        "$eq": 145
                    }
                },
                "direction": "forward" /*  与索引无关,查询方向,backward 则相反  */
            }
        },
        "rejectedPlans": []   /*  非最优而被查询优化器reject的  */
    },
    "executionStats": {
        "executionSuccess": true,
        "nReturned": 0,  /*  返回结果总数  */
        "executionTimeMillis": 0, /*  执行总耗时,毫秒 */
        "totalKeysExamined": 0, /*  通过索引命中的总数,或是索引扫描的总数  */
        "totalDocsExamined": 849, /*  文档扫描总数  */
        "executionStages": { /*  最后一阶段执行的详细信息  */
            "stage": "PROJECTION",   /*  最后一阶段的执行类型   */
            "nReturned": 0,
            "executionTimeMillisEstimate": 0,   /*  totalDocsExamined 的耗时   */
            "works": 851,
            "advanced": 0,
            "needTime": 850,
            "needYield": 0,
            "saveState": 6,
            "restoreState": 6,
            "isEOF": 1,
            "invalidates": 0,
            "transformBy": {
                "examBaseId": 1,
                "_id": 0
            },
            "inputStage": {   /* 第一阶段执行的详细信息  */
                "stage": "COLLSCAN", /*  执行该阶段所用的类型,COLLSCAN 全表扫描  */
                "filter": {
                    "schoolId": {
                        "$eq": 145
                    }
                },
                "nReturned": 0,
                "executionTimeMillisEstimate": 0, /*  totalKeysExamined 的耗时   */
                "works": 851,
                "advanced": 0,
                "needTime": 850,
                "needYield": 0,
                "saveState": 6,
                "restoreState": 6,
                "isEOF": 1,
                "invalidates": 0,
                "direction": "forward",
                "docsExamined": 849
            }
        },
        "allPlansExecution": []   /* 所有索引在并行执行的详细结果  */
    },
    "serverInfo": {
        "version": "3.4.7",
        "gitVersion": "cf38c1b8a0a8dca4a11737581beafef4fe120bcd"
    },
    "ok": 1
}

 

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
打赏
0 评论
2 收藏
0
分享
返回顶部
顶部