在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
}