MongoDB执行计划获取(db.collection.explain())

在RDBMS中,无论那种数据库,都提供了SQL剖析工具,用来解决SQL效率低下的问题。在MongoDB中,也有相应的策略来实现剖析。MongoDB提供了db.collection.explain()方法, cursor.explain()方法,和explain命令去返回查询计划信息和查询计划的执行统计信息。这为我们诊断查询提供了极大的便利,本文主要描述db.collection.explain()的相关用法。

一、db.collection.explain()简介

    支持下列操作返回查询计划
            aggregate(); count(); distinct(); find(); group(); remove(); update() 
    cursor.explain(verbosity)   为一个游标返回其查询执行计划(Reports on the query execution plan for a cursor)
    cursor.explain(verbosity) 最通常的行式为db.collection.find().explain()。其中verbosity说明返回信息的粒度。

    执行计划中几类常见的操作描述
            COLLSCAN 全表扫描        
    IXSCAN 索引扫描          
    FETCH 根据索引去检索文档 
    SHARD_MERGE 合并分片结果 

    db.collection.find().explain(verbose)
            explain()输出一个以文档形式展现的执行计划,可以包括统计信息(可选)。

    参数verbose:
            可选参数。缺省值为queryPlanner,用于查看指定执行计划的特定部分。即给定不同的参数则输出信息的详细程度不同
            常用的包括queryPlanner,executionStats,以及allPlansExecution

    queryPlanner模式
            这个是缺省模式。
            MongoDB运行查询优化器对当前的查询进行评估并选择一个最佳的查询计划

    executionStats模式        
            mongoDB运行查询优化器对当前的查询进行评估并选择一个最佳的查询计划进行执行
            在执行完毕后返回这个最佳执行计划执行完成时的相关统计信息
            对于写操作db.collection.explain()返回关于更新和删除操作的信息,但是并不将修改应用到数据库
            对于那些被拒绝的执行计划,不返回其统计信息

    allPlansExecution模式
            该模式是前2种模式的更细化,即会包括上述2种模式的所有信息
            即按照最佳的执行计划执行以及列出统计信息,而且还会列出一些候选的执行计划
            如果有多个查询计划   ,executionStats信息包括这些执行计划的部分统计信息

    db.collection.explain().find()
            该方法与db.collection.find().explain()类似,但是存在以下关键差异

            The db.collection.explain() method wraps the explain command and is the preferred way to run explain.
            db.collection.explain().find() is similar to db.collection.find().explain() with the following key differences:

            The db.collection.explain().find() construct allows for the additional chaining of query modifiers. 
            For list of query modifiers, see db.collection.explain().find().help().

            The db.collection.explain().find() returns a cursor, which requires a call to .next(), or its alias .finish(), 
            to return the explain() results. If run interactively in the mongo shell, the mongo shell
             automatically calls .finish() to return the results. For scripts, however, you must explicitly call .next(), 
             or .finish(), to return the results. For list of cursor-related methods, see db.collection.explain().find().help().
            db.collection.explain().aggregate() is equivalent to passing the explain option to the db.collection.aggregate() method.

    //获取explain的支持的运算方法
    > db.collection.explain().help()
    Explainable operations
            .aggregate(...) - explain an aggregation operation
            .count(...) - explain a count operation
            .distinct(...) - explain a distinct operation
            .find(...) - get an explainable query
            .findAndModify(...) - explain a findAndModify operation
            .group(...) - explain a group operation
            .remove(...) - explain a remove operation
            .update(...) - explain an update operation
    Explainable collection methods
            .getCollection()
            .getVerbosity()
            .setVerbosity(verbosity)

    //获取explain().find()支持的运算方法 
    > db.collection.explain().find().help()
    Explain query methods
            .finish() - sends explain command to the server and returns the result
            .forEach(func) - apply a function to the explain results
            .hasNext() - whether this explain query still has a result to retrieve
            .next() - alias for .finish()
    Explain query modifiers
            .addOption(n)
            .batchSize(n)
            .comment(comment)
            .count()
            .hint(hintSpec)
            .limit(n)
            .maxTimeMS(n)
            .max(idxDoc)
            .min(idxDoc)
            .readPref(mode, tagSet)
            .showDiskLoc()
            .skip(n)
            .snapshot()
            .sort(sortSpec)
    >       

二、演示相关用法

1、演示db.collection.explain().count()执行计划

//如前面的获取的帮助可知,可以通过db.collection.explain()方式查看相关聚合运算的执行计划,如下:
        > db.version()
        3.2.10
        > db.users.explain().count()
        {
                "queryPlanner" : {
                        "plannerVersion" : 1,
                        "namespace" : "test.users",
                        "indexFilterSet" : false,
                        "winningPlan" : {
                                "stage" : "COUNT"
                        },
                        "rejectedPlans" : [ ]
                },
                "serverInfo" : {
                        "host" : "node233.edq.com",
                        "port" : 27017,
                        "version" : "3.2.10",
                        "gitVersion" : "79d9b3ab5ce20f51c272b4411202710a082d0317"
                },
                "ok" : 1
        }

        //注意,下面将explain()放置到count()之后提示错误 
        > db.users.count().explain()
        2016-12-08T16:53:22.760+0800 E QUERY    [thread1] TypeError: db.users.count(...).explain is not a function :
        @(shell):1:1

2、演示db.collection.explain().update()用法

//先插入一个文档
> db.example.insert({id:1,ename:"leshami",blog:"http://blog.csdn.net/leshami"})
WriteResult({ "nInserted" : 1 })
//下面查看update的执行计划
> db.example.explain().update({id:1},{$set:{name:"robinson_0612"}})
{
        "queryPlanner" : {
                "plannerVersion" : 1,
                "namespace" : "test.example",
                "indexFilterSet" : false,
                "parsedQuery" : {
                        "id" : {
                                "$eq" : 1
                        }
                },
                "winningPlan" : {
                        "stage" : "UPDATE",
                        "inputStage" : {
                                "stage" : "COLLSCAN",
                                "filter" : {
                                        "id" : {
                                                "$eq" : 1
                                        }
                                },
                                "direction" : "forward"
                        }
                },
                "rejectedPlans" : [ ]
        },
        "serverInfo" : {
                "host" : "node233.edq.com",
                "port" : 27017,
                "version" : "3.2.10",
                "gitVersion" : "79d9b3ab5ce20f51c272b4411202710a082d0317"
        },
        "ok" : 1
}

//再次查看文档,如下,文档并没有被更新,正如前文所述,该方式并不将修改应用到数据库
> db.example.find().pretty()
{
        "_id" : ObjectId("584924b4de4a7c9eeef9ef9d"),
        "id" : 1,
        "ename" : "leshami",
        "blog" : "http://blog.csdn.net/leshami"
}

//同样将update前置到explain之前也是错误的
> db.example.update({id:1},{$set:{name:"robinson_0612"}}).explain()
2016-12-08T17:24:24.708+0800 E QUERY    [thread1] TypeError: db.example.update(...).explain is not a function :
@(shell):1:1

3、执行计划相关描述

//演示演示集合文档数据,可以参考:http://blog.csdn.net/leshami/article/details/52672310
//从下面的查询结果可知,缺省情况下,explain包括2个部分,一个是queryPlanner,一个是serverInfo
//如果使用了executionStats或者allPlansExecution,则还会返回executionStats信息
> db.persons.find({age:26}).explain()
{
        "queryPlanner" : {
                "plannerVersion" : 1,              //查询计划版本
                "namespace" : "test.persons",      //被查询对象
                "indexFilterSet" : false,          //是否使用到了索引来过滤
                "parsedQuery" : {                  //解析查询,即过滤条件是什么
                        "age" : {                  //此处为age=26
                                "$eq" : 26
                        }
                },
                "winningPlan" : {                  //最佳的执行计划
                        "stage" : "COLLSCAN",      //COLLSCAN为集合扫描
                        "filter" : {               //过滤条件
                                "age" : {
                                        "$eq" : 26
                                }
                        },
                        "direction" : "forward"    //方向:forward
                },
                "rejectedPlans" : [ ]              //拒绝的执行计划,此处没有
        },
        "serverInfo" : {                           //服务器信息,包括主机名,端口,版本等。
                "host" : "node233.edq.com",
                "port" : 27017,
                "version" : "3.2.10",
                "gitVersion" : "79d9b3ab5ce20f51c272b4411202710a082d0317"
        },
        "ok" : 1
}

4、执行计划统计信息描述

//下面查看executionStats,使用一个大的集合,以便观察执行统计信息
> db.inventory.find({id:500}).explain("executionStats")
{
        "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://docs.mongodb.com/manual/reference/explain-results/#executionstats

5、获取所有的执行计划

//如下所示,由于当前我们没有多个执行计划,因此仅返回一个执行计划
> db.persons.find({age:26}).explain("allPlansExecution")
{
        "queryPlanner" : {
                "plannerVersion" : 1,
                "namespace" : "test.persons",
                "indexFilterSet" : false,
                "parsedQuery" : {
                        "age" : {
                                "$eq" : 26
                        }
                },
                "winningPlan" : {
                        "stage" : "COLLSCAN",
                        "filter" : {
                                "age" : {
                                        "$eq" : 26
                                }
                        },
                        "direction" : "forward"
                },
                "rejectedPlans" : [ ]
        },
        "executionStats" : {
                "executionSuccess" : true,
                "nReturned" : 3,
                "executionTimeMillis" : 0,
                "totalKeysExamined" : 0,
                "totalDocsExamined" : 11,
                "executionStages" : {
                        "stage" : "COLLSCAN",
                        "filter" : {
                                "age" : {
                                        "$eq" : 26
                                }
                        },
                        "nReturned" : 3,
                        "executionTimeMillisEstimate" : 0,
                        "works" : 13,
                        "advanced" : 3,
                        "needTime" : 9,
                        "needYield" : 0,
                        "saveState" : 0,
                        "restoreState" : 0,
                        "isEOF" : 1,
                        "invalidates" : 0,
                        "direction" : "forward",
                        "docsExamined" : 11
                },
                "allPlansExecution" : [ ]
        },
        "serverInfo" : {
                "host" : "node233.edq.com",
                "port" : 27017,
                "version" : "3.2.10",
                "gitVersion" : "79d9b3ab5ce20f51c272b4411202710a082d0317"
        },
        "ok" : 1
}                   

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏程序员的SOD蜜

EF+MySQL乐观锁控制电商并发下单扣减库存,在高并发下的问题

下订单减库存的方式 现在,连农村的大姐都会用手机上淘宝购物了,相信电商对大家已经非常熟悉了,如果熟悉电商开发的同学,就知道在买家下单购买商品的时候,是需要扣减库...

7158
来自专栏大内老A

在Entity Framework中使用存储过程(一):实现存储过程的自动映射

之前给自己放了一个比较长的假期,在这期间基本上没怎么来园子逛。很多朋友的留言也没有一一回复,在这里先向大家道个歉。最近一段时间的工作任务是如何将ADO.NET ...

3515
来自专栏互联网技术栈

Elasticsearch之元数据(meta-fields)介绍

在Elasticsearch下,一个文档除了有数据之外,它还包含了元数据(Metadata)。每创建一条数据时,都会对元数据进行写入等操作,当然有些元数据是在创...

1626
来自专栏皮皮之路

【MySQL】通过Binary Log简单实现数据回滚(一)

2926
来自专栏张善友的专栏

Ibatis in action 电子书

电子书 ? 是ibatis 项目组写的ibatis开发的权威书籍.现在只有电子版,目前使用Java作为描述的平台,这个书对于.NET和Ruby一样适用.    ...

2147
来自专栏Python中文社区

进击的爬虫:用Python搭建匿名代理池

專 欄 ❈ 苍冥,Python中文社区专栏作者,澳洲华裔,目前在墨尔本某国际咨询公司任职Splunk Developer,擅长网络安全及攻防,热爱Python...

3725
来自专栏钱曙光的专栏

MySQL 8 新特性介绍

广受欢迎的开源数据库MySQL 8中,包括了众多新特性,其中包括对Unicode更好的支持、对JSON格式和文档的处理,以及一直以来呼吁增加的象window函数...

5560
来自专栏c#开发者

Oracle9i第2版中的UNT_FILE提高了文件输入/输出(I/O)功能。

技术 PL/SQL 提高文件操作功能 作者:Steven Feuerstein Oracle9i第2版中的UNT_FILE提高了文件输入/输出(I/O)功...

3384
来自专栏互联网高可用架构

初识分库分表框架DBSPLIT

1764
来自专栏乐沙弥的世界

MySQL Utilities工具包概述及安装

2511

扫码关注云+社区

领取腾讯云代金券