MongoDB分析工具之一:explain()语句分析工具

explain(),语句分析工具

MongoDB 3.0之后,explain的返回与使用方法与之前版本有了很大的变化,介于3.0之后的优秀特色和我们目前所使用给的是3.0.7版本,本文仅针对MongoDB 3.0+的explain进行讨论。3.0+的explain有三种模式,分别是:queryPlanner、executionStats、allPlansExecution。现实开发中,常用的是executionStats模式,主要分析这种模式。

 基本用法

先来看一个基本用法:

db.duan.find({x:1}).explain()

explain()添加不同参数

explain()也接收不同的参数,通过设置不同参数我们可以查看更详细的查询计划。

  • queryPlanner:queryPlanner是默认参数,添加queryPlanner参数的查询结果就是我们上面表格中看到的查询结果。
  • executionStats:executionStats会返回最佳执行计划的一些统计信息。
  • allPlansExecution:allPlansExecution用来获取所有执行计划,结果参数基本与上文相同。

1、queryPlanner,这个是explain()默认参数

直接跟在find()函数后面,表示查看find()函数的执行计划,结果如下:

> db.duan.find({x:1}).explain()
{
    "queryPlanner" : {
        "plannerVersion" : 1,
        "namespace" : "member_data.duan",
        "indexFilterSet" : false,
        "parsedQuery" : {
            "x" : {
                "$eq" : 1
            }
        },
        "winningPlan" : {
            "stage" : "COLLSCAN",
            "filter" : {
                "x" : {
                    "$eq" : 1
                }
            },
            "direction" : "forward"
        },
        "rejectedPlans" : [ ]
    },
    "serverInfo" : {
        "host" : "localhost.localdomain",
        "port" : 27017,
        "version" : "4.0.6",
        "gitVersion" : "caa42a1f75a56c7643d0b68d3880444375ec42e3"
    },
    "ok" : 1
}
> 

返回结果包含两大块信息,一个是queryPlanner,即查询计划,还有一个是serverInfo,即MongoDB服务的一些信息。那么这里涉及到的参数比较多,queryPlanner结果参数说明如下:

参数                      
含义                                                                                                       
plannerVersion 查询计划版本
namespace    要查询的集合(该值返回的是该query所查询的表)
indexFilterSet    是否使用索引(针对该query是否有indexfilter)
parsedQuery    查询条件,此处为x=1
winningPlan    最佳执行计划
winningPlan.stage   

最优执行计划的stage(查询方式),常见的有:

COLLSCAN/全表扫描:(应该知道就是CollectionScan,就是所谓的“集合扫描”,和mysql中table scan/heap scan类似,这个就是所谓的性能最烂最无奈的由来

)、

IXSCAN/索引扫描:(而是IndexScan,这就说明我们已经命中索引了)、

FETCH/根据索引去检索文档、SHARD_MERGE/合并分片结果、IDHACK/针对_id进行查询

winningPlan.inputStage 用来描述子stage,并且为其父stage提供文档和索引关键字。
winningPlan.stage的child stage 此处是IXSCAN,表示进行的是index scanning。
winningPlan.keyPattern 所扫描的index内容,此处是did:1,status:1,modify_time: -1与scid : 1
winningPlan.indexName winning plan所选用的index。
winningPlan.isMultiKey 是否是Multikey,此处返回是false,如果索引建立在array上,此处将是true。
winningPlan.direction 此query的查询顺序,此处是forward,如果用了.sort({modify_time:-1})将显示backward。
filter    过滤条件
winningPlan.indexBounds winningplan所扫描的索引范围,如果没有制定范围就是[MaxKey, MinKey],这主要是直接定位到mongodb的chunck中去查找数据,加快数据读取。
rejectedPlans    拒绝的执行计划(其他执行计划(非最优而被查询优化器reject的)的详细返回,其中具体信息与winningPlan的返回中意义相同,故不在此赘述)      
serverInfo    MongoDB服务器信息
   

2、executionStats参数

executionStats会返回最佳执行计划的一些统计信息,如下:

> db.duan.find({x:1}).explain("executionStats")
{
    "queryPlanner" : {
        "plannerVersion" : 1,
        "namespace" : "member_data.duan",
        "indexFilterSet" : false,
        "parsedQuery" : {
            "x" : {
                "$eq" : 1
            }
        },
        "winningPlan" : {
            "stage" : "COLLSCAN",
            "filter" : {
                "x" : {
                    "$eq" : 1
                }
            },
            "direction" : "forward"
        },
        "rejectedPlans" : [ ]
    },
    "executionStats" : {
        "executionSuccess" : true,
        "nReturned" : 0,
        "executionTimeMillis" : 0,
        "totalKeysExamined" : 0,
        "totalDocsExamined" : 3,
        "executionStages" : {
            "stage" : "COLLSCAN",
            "filter" : {
                "x" : {
                    "$eq" : 1
                }
            },
            "nReturned" : 0,
            "executionTimeMillisEstimate" : 0,
            "works" : 5,
            "advanced" : 0,
            "needTime" : 4,
            "needYield" : 0,
            "saveState" : 0,
            "restoreState" : 0,
            "isEOF" : 1,
            "invalidates" : 0,
            "direction" : "forward",
            "docsExamined" : 3
        }
    },
    "serverInfo" : {
        "host" : "localhost.localdomain",
        "port" : 27017,
        "version" : "4.0.6",
        "gitVersion" : "caa42a1f75a56c7643d0b68d3880444375ec42e3"
    },
    "ok" : 1
}
> 
这里除了我们上文介绍到的一些参数之外,还多了executionStats参数,含义如下:
参数                      含义                                                                                                        
executionSuccess    是否执行成功
nReturned    返回的结果数
executionTimeMillis    执行耗时
totalKeysExamined    索引扫描次数
totalDocsExamined    文档扫描次数
executionStages    这个分类下描述执行的状态
stage    扫描方式,具体可选值与上文的相同
nReturned    查询结果数量
executionTimeMillisEstimate    预估耗时
works    工作单元数,一个查询会分解成小的工作单元
advanced    优先返回的结果数
docsExamined    文档检查数目,与totalDocsExamined一致。检查了总共的个documents,而从返回上面的nReturne数量
   
   

常见用法:

    第一层,executionTimeMillis

    最为直观explain返回值是executionTimeMillis值,指的是我们这条语句的执行时间,这个值当然是希望越少越好。

    其中有3个executionTimeMillis,分别是:

    executionStats.executionTimeMillis

    该query的整体查询时间。

    executionStats.executionStages.executionTimeMillisEstimate

    该查询根据index去检索document获得2001条数据的时间。

    executionStats.executionStages.inputStage.executionTimeMillisEstimate

    该查询扫描2001行index所用时间。

    第二层,index与document扫描数与查询返回条目数

    这个主要讨论3个返回项,nReturned、totalKeysExamined、totalDocsExamined,分别代表该条查询返回的条目、索引扫描条目、文档扫描条目。

    这些都是直观地影响到executionTimeMillis,我们需要扫描的越少速度越快。

    对于一个查询,我们最理想的状态是:

    nReturned=totalKeysExamined=totalDocsExamined

    第三层,stage状态分析

    那么又是什么影响到了totalKeysExamined和totalDocsExamined?是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的组合(查询的时候尽可能用上索引):

    Fetch+IDHACK

    Fetch+ixscan

    Limit+(Fetch+ixscan)

    PROJECTION+ixscan

    SHARDING_FITER+ixscan

    COUNT_SCAN

SORT_KEY_GENERATOR

    不希望看到包含如下的stage:

    COLLSCAN(全表扫描),SORT(使用sort但是无index),不合理的SKIP,SUBPLA(未用到index的$or),COUNTSCAN(不使用index进行count)

3、allPlansExecution参数

示例:

> db.duan.find({x:1}).explain("allPlansExecution")
{
    "queryPlanner" : {
        "plannerVersion" : 1,
        "namespace" : "member_data.duan",
        "indexFilterSet" : false,
        "parsedQuery" : {
            "x" : {
                "$eq" : 1
            }
        },
        "winningPlan" : {
            "stage" : "COLLSCAN",
            "filter" : {
                "x" : {
                    "$eq" : 1
                }
            },
            "direction" : "forward"
        },
        "rejectedPlans" : [ ]
    },
    "executionStats" : {
        "executionSuccess" : true,
        "nReturned" : 0,
        "executionTimeMillis" : 0,
        "totalKeysExamined" : 0,
        "totalDocsExamined" : 3,
        "executionStages" : {
            "stage" : "COLLSCAN",
            "filter" : {
                "x" : {
                    "$eq" : 1
                }
            },
            "nReturned" : 0,
            "executionTimeMillisEstimate" : 0,
            "works" : 5,
            "advanced" : 0,
            "needTime" : 4,
            "needYield" : 0,
            "saveState" : 0,
            "restoreState" : 0,
            "isEOF" : 1,
            "invalidates" : 0,
            "direction" : "forward",
            "docsExamined" : 3
        },
        "allPlansExecution" : [ ]
    },
    "serverInfo" : {
        "host" : "localhost.localdomain",
        "port" : 27017,
        "version" : "4.0.6",
        "gitVersion" : "caa42a1f75a56c7643d0b68d3880444375ec42e3"
    },
    "ok" : 1
}
>

聚合类的查询的explain()分析:

db.getCollection('my_order').explain().aggregate([
        {$match: {"sender.id" : "8F8184E0ECF24B83980FE96357E7B746","status" : 1}},
        {$group:{_id:"$sender.id",totalAmount:{$sum:"$amount"},quantity:{$sum:1}}}        
])

结果示例说明:

{
        "queryPlanner" : {
             .........
        },
        "executionStats" : {                   //执行计划相关统计信息
                "executionSuccess" : true,     //执行成功的状态
                "nReturned" : 1,               //返回结果集数目
                "executionTimeMillis" : 21896, //执行所需的时间,毫秒
                "totalKeysExamined" : 0,       //索引检查的时间
                "totalDocsExamined" : 5000000, //检查文档总数
                "executionStages" : {          
                        "stage" : "COLLSCAN",  //使用集合扫描方式
                        "filter" : {           //过滤条件
                                "id" : {
                                        "$eq" : 500
                                }
                        },
                        "nReturned" : 1,       //返回结果集数目  
                        "executionTimeMillisEstimate" : 19230, //预估的执行时间,毫秒
                        "works" : 5000002,   //工作单元数,一个查询会被派生为一些小的工作单元
                        "advanced" : 1,      //优先返回的结果数目
                        "needTime" : 5000000,
                        "needYield" : 0,
                        "saveState" : 39065,
                        "restoreState" : 39065,
                        "isEOF" : 1,
                        "invalidates" : 0,
                        "direction" : "forward",   //方向
                        "docsExamined" : 5000000   //文档检查数目
                }
        },
        "serverInfo" : {
         ...........
        "ok" : 1
}
 
原文地址:https://www.cnblogs.com/duanxz/p/3768253.html