mongoDB 3.0+ explain

2018-02-08 10:24:39来源:oschina作者:learn_more人点击

分享

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
}

最新文章

123

最新摄影

微信扫一扫

第七城市微信公众平台