背景

mongodb 提供了类sql的数据查询及操作方式,同时也包含了聚合操作、索引等多个机制;

按以往的经验,不当的库表操作或索引模式往往会造成许多问题,如查询操作缓慢、数据库吞吐量低下、CPU或磁盘IO飙升等问题。

因此在应用开发过程中,有必要对DB操作进行审视,尤其是关键业务或复杂条件查询。mongodb 提供了explain方法可以让我们

对 DB查询语句进行分析,提前分析潜在的瓶颈。

查询计划

mongodb 通过查询计划(QueryPlan)描述一个查询语句的执行过程,而通常一个查询操作可能对应多组查询计划。

这些查询计划通过选举机制产生最优计划,作为最终的执行方案。此外mongodb 还提供了查询计划的缓存机制,如下图:

https://docs.mongodb.com/manual/_images/query-planner-diagram.bakedsvg.sv

Diagram of query planner logic

查询操作被映射到一个查询模型(query shape),模型中会包含条件(predicate)、排序(sort)、投影(projection)的定义;

以查询模型作为Key查找已存在的Plan缓存,在找到缓存的下一步仍进一步评估查询性能,若性能评估结果未达标,则 mongodb会淘汰缓存并进入查询计划生成阶段。

每一个计划生成阶段都会包含:

  • 产生候选计划;
  • 评估优选计划;
  • 竞选最优计划;
  • 创建缓存;

在产生最优计划之后,查询执行器将执行当前计划并产生最终结果。

explain 操作

通过下面的语句,可以对当前查询计划展开分析

db.T_FooData.find({
"appId":"s5WrMmrJV_8RBJG17FSVoY995Kga",
"nodeType":"SENSOR",
"creationTime":{
$gte : ISODate("2017-08-08T10:34:33.125Z"),
$lt : ISODate("2017-08-08T12:34:33.125Z")
}
}).explain("executionStats")

输出结果

{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "db.T_FooData",
"indexFilterSet" : false,
"parsedQuery" : {
"$and" : [
{
"appId" : {
"$eq" : "s5WrMmrJV_8RBJG17FSVoY995Kga"
}
},
{
"nodeType" : {
"$eq" : "SENSOR"
}
},
{
"creationTime" : {
"$lt" : ISODate("2017-08-08T12:34:33.125Z")
}
},
{
"creationTime" : {
"$gte" : ISODate("2017-08-08T10:34:33.125Z")
}
}
]
},
"winningPlan" : { ... },
"rejectedPlans" : [ ... ],
},
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 62848,
"executionTimeMillis" : 3058,
"totalKeysExamined" : 1510833,
"totalDocsExamined" : 1510833,
"executionStages" : { ... } },
"serverInfo" : {
"host" : "NB3000W_MongoDB_01",
"port" : 50001,
"version" : "3.4.7",
"gitVersion" : "4249c1d2b5999ebbf1fdf3bc0e0e3b3ff5c0aaf2"
},
"ok" : 1,
"$gleStats" : {
"lastOpTime" : Timestamp(1504498101, 1),
"electionId" : ObjectId("7fffffff0000000000000001")
}
}

结果说明

  • queryPlanner 描述当前的查询计划;

  • queryPlanner.namespace 描述当前的集合命名空间,{db}.{collectionName}

  • queryPlanner.indexFilterSet 是否设置了indexFilter,Filter决定了查询优化器对于某个查询将如何使用索引

  • queryPlanner.parsedQuery 解析后的查询信息

  • queryPlanner.winningPlan 最优计划

  • queryPlanner.rejectPlans 拒绝的计划列表

  • executionStats 执行过程统计,捕获计划在执行过程中的相关信息

  • executionStats.executionSuccess 是否执行成功

  • executionStats.nReturned 返回条目数量

  • executionStats.executionTimeMilis 执行时间(ms)

  • executionStats.totalKeysExamined 索引检测条目

  • executionStats.totalDocsExamined 文档检测条目

  • executionStats.executionStages 执行阶段详情

explain 模式

mongodb 为 explain 操作提供了几种模式:

  • queryPlanner 默认的模式,仅进行查询计划分析,无法输出执行过程统计;
  • executionStats 执行模式,在查询计划分析后,将执行winningPlan并统计过程信息;
  • allPlansExecution 全计划执行模式,将执行所有计划(包括rejectPlans),并返回过程统计信息;

    executionStats.allPlansExecution 包含了所有计划(除winningPlan之外)的执行过程统计信息

执行计划详解

执行计划将整个过程分解为多个阶段,阶段(stage)以树状结构组织,这点与执行过程是匹配的。

stage 分为多种类型,如下:

阶段 描述
COLLSCAN 全表扫描
IXSCAN 索引扫描
FETCH 根据索引去检索指定document
PROJECTION 限定返回字段
SHARD_MERGE 将各个分片返回数据进行merge
SORT 表明在内存中进行了排序
LIMIT 使用limit限制返回数
SKIP 使用skip进行跳过
IDHACK 针对_id进行查询
SHARDING_FILTER 通过mongos对分片数据进行查询
COUNT 利用db.coll.explain().count()之类进行count运算
COUNTSCAN count不使用用Index进行count
COUNT_SCAN count使用了Index进行count
SUBPLA 未使用到索引的$or查询
TEXT 使用全文索引进行查询

winningPlan 样例

"winningPlan" : {
"stage" : "FETCH",
"filter" : {
"$and" : [
{
"nodeType" : {
"$eq" : "GATEWAY"
}
},
{
"creationTime" : {
"$lt" : ISODate("2017-08-08T12:34:33.125Z")
}
},
{
"creationTime" : {
"$gte" : ISODate("2017-08-08T10:34:33.125Z")
}
}
]
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"appId" : 1
},
"indexName" : "appId",
"isMultiKey" : false,
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 1,
"direction" : "forward",
"indexBounds" : {
"appId" : [
"[\"s5WrMmrJV_8RBJG17FSVoY995Kga\", \"s5WrMmrJV_8RBJG17FSVoY995Kga\"]"
]
}
}
},

字段说明

属性 描述
winningPlan.stage 最优计划stage,FETCH表示根据索引检索文档
winningPlan.filter 最优计划的过滤器,即查询条件
winningPlan.inputStage 最优计划stage的child stage
winningPlan.inputStage.stage child stage,此处是IXSCAN,表示进行index scanning
winningPlan.inputStage.keyPattern 扫描的索引模式
winningPlan.inputStage.indexName 选用索引名称
winningPlan.inputStage.isMultiKey 是否是Multikey,如果索引建立在array上则为true
winningPlan.inputStage.isSparse 是否稀疏索引
winningPlan.inputStage.isPartial 是否分区索引
winningPlan.inputStage.direction 此query的查询顺序,此处是forward,如果用了.sort({w:-1})将显示backward
winningPlan.inputStage.indexBounds 所扫描的索引范围

过程统计详解

executionStats 样例

"executionStats" : {
"executionSuccess" : true,
"nReturned" : 62848,
"executionTimeMillis" : 3058,
"totalKeysExamined" : 1510833,
"totalDocsExamined" : 1510833,
"executionStages" : {
"stage" : "FETCH",
"filter" : {
"$and" : [
{
"nodeType" : {
"$eq" : "GATEWAY"
}
},
{
"creationTime" : {
"$lt" : ISODate("2017-08-08T12:34:33.125Z")
}
},
{
"creationTime" : {
"$gte" : ISODate("2017-08-08T10:34:33.125Z")
}
}
]
},
"nReturned" : 62848,
"executionTimeMillisEstimate" : 2765,
"works" : 1510834,
"advanced" : 62848,
"needTime" : 1447985,
"needYield" : 0,
"saveState" : 11807,
"restoreState" : 11807,
"isEOF" : 1,
"invalidates" : 0,
"docsExamined" : 1510833,
"alreadyHasObj" : 0,
"inputStage" : {
"stage" : "IXSCAN",
"nReturned" : 1510833,
"executionTimeMillisEstimate" : 792,
"works" : 1510834,
"advanced" : 1510833,
"needTime" : 0,
"needYield" : 0,
"saveState" : 11807,
"restoreState" : 11807,
"isEOF" : 1,
"invalidates" : 0,
"keyPattern" : {
"appId" : 1
},
"indexName" : "appId",
"isMultiKey" : false,
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 1,
"direction" : "forward",
"indexBounds" : {
"appId" : [
"[\"s5WrMmrJV_8RBJG17FSVoY995Kga\", \"s5WrMmrJV_8RBJG17FSVoY995Kga\"]"
]
},
"keysExamined" : 1510833,
"dupsTested" : 0,
"dupsDropped" : 0,
"seenInvalidated" : 0
}
}
}

字段说明

属性 描述
executionStats.executionSuccess 是否执行成功
executionStats.nReturned 返回条目数量
executionStats.executionTimeMilis 执行时间(ms)
executionStats.totalKeysExamined 索引检测条目
executionStats.totalDocsExamined 文档检测条目
executionStats.executionStages 执行阶段详情,大部分字段继承于winningPlan.inputStage
executionStats.executionStages.stage 执行阶段,FETCH表示根据索引获取文档
executionStats.executionStages.nReturned 阶段返回条目数量
executionStats.executionStages.executionTimeMillisEstimate 阶段执行时间
executionStats.executionStages.docsExamined 阶段中文档检测条目
executionStats.executionStages.works 阶段中扫描任务数
executionStats.executionStages.advanced 阶段中向上提交数量
executionStats.executionStages.needTime 阶段中定位索引位置所需次数
executionStats.executionStages.needYield 阶段中获取锁等待时间
executionStats.executionStages.isEOF 阶段中是否到达流的结束位,对于limit限制符的查询可能为0
executionStats.executionStages.inputStage 执行阶段的子阶段,这里是一个IXSCAN的子过程

参考文档

explain 官方说明

http://www.mongoing.com/eshu_explain1

https://docs.mongodb.com/manual/reference/explain-results/#explain-output

关于explain的几种模式

https://docs.mongodb.com/manual/reference/method/cursor.explain/

理解mongo 的查询行为

https://www.compose.com/articles/explain-explain-understanding-mongo-query-behavior/

mongo的查询缓存

https://docs.mongodb.com/manual/core/query-plans/#index-filters

mongodb查询操作分析的更多相关文章

  1. MongoDB查询转对象是出错Element '_id' does not match any field or property of class

    MongoDB查询转对象是出错Element '_id' does not match any field or property of class   解决方法: 1.在实体类加:[BsonIgno ...

  2. MongoDB查询操作限制返回字段的方法

    这篇文章主要介绍了MongoDB查询操作限制返回字段的方法,需要的朋友可以参考下   映射(projection )声明用来限制所有查询匹配文档的返回字段.projection以文档的形式列举结果集中 ...

  3. mongodb查询文档

    说到查询,我们一般就想起了关系型数据库的查询了,比如:order by(排序).limit(分页).范围查询(大于某个值,小于某个值..,in查询,on查询,like查询等待很多),同样mongodb ...

  4. [转]mongodb 查询条件:关系运算符"$lt", "$lte", "$gt", "$gte", "$ne" 逻辑运算符"$and“, "$or“, "$nor“

    mongodb 查询条件   这节来说说mongodb条件操作符,"$lt", "$lte", "$gt", "$gte" ...

  5. Mongodb查询的用法,备注防止忘记

    最近在用这个东西,为防止忘记,记下来. 集合简单查询方法 mongodb语法:db.collection.find()  //collection就是集合的名称,这个可以自己进行创建. 对比sql语句 ...

  6. mongodb查询关于大于小于的用法;

    mongoDB查询操作符: http://www.runoob.com/mongodb/mongodb-operators.html 项目中需要的场景是这样的,每个人每天只能领取一张明信片,换句话说, ...

  7. MongoDB查询分析

    MongoDB 查询分析可以确保我们建立的索引是否有效,是查询语句性能分析的重要工具.MongoDB 查询分析常用函数有:explain() 和 hint(). 1. explain(): 提供查询信 ...

  8. 【mongodb系统学习之十】mongodb查询(一)

    十.mongodb查询:find ;查询时条件中不能引用文档中其他键的值: 1).查询数据库全部数据:语法db.collectionName.find();默认只显示前20条,如图: 2).按条件查询 ...

  9. MongoDb进阶实践之三 MongoDB查询命令详述

    一.引言           上一篇文章我们已经介绍了MongoDB数据库的最基本操作,包括数据库的创建.使用和删除数据库,文档的操作也涉及到了文档的创建.删除.更新和查询,当然也包括集合的创建.重命 ...

随机推荐

  1. JavaScript获取当前url路径

    1.假设当前页完整地址是:http://localhost:61768/Home/Index?id=2 //获取当前窗口的Url var url = window.location.href; //结 ...

  2. 使用JSON JavaScriptSerializer进行反序列化和序列化时报错,字符的长度超出了MaxJsonLength的长度

    文章参考:https://www.cnblogs.com/wangfuyou/p/6046307.html 1.解决办法是在web.config增加如下节点到<configuration> ...

  3. CTF---密码学入门第五题 传统知识+古典密码

    传统知识+古典密码分值:10 来源: 霜羽 难度:易 参与人数:2297人 Get Flag:735人 答题人数:938人 解题通过率:78% 小明某一天收到一封密信,信中写了几个不同的年份     ...

  4. HUST 1585 排队

    1585 - 排队 时间限制:1秒 内存限制:128兆 351 次提交 179 次通过 题目描述 BG站在一个有n个人的队伍中,但他并不知道他处于队伍中的哪个位置,他向前向后观察,只能断定他的前方有至 ...

  5. hdu_2030

    一个小小知识点,统计字符串中汉字出现的次数,直接给出代码 //ASCII码的范围是0-127所以,超出范围的都是汉字,因为一个汉字占两个字符位置,所以结果除以2就可以了 #include<cst ...

  6. hdu_1014(竟然真的还有更水的)

    注意输出就没了... #include<cstdio> #include<cstring> using namespace std; int gcd(int a, int b) ...

  7. mysql常用的提权方法

    一,利用MOF提权 Windows 管理规范 (WMI) 提供了以下三种方法编译到 WMI 存储库的托管对象格式 (MOF) 文件: 方法 1: 运行 MOF 文件指定为命令行参数将 Mofcomp. ...

  8. redis3.0 集群在windows上的配置(转)

    1. 安装Redis版本:win-3.0.501https://github.com/MSOpenTech/redis/releases页面有,我下载的是zip版本的:Redis-x64-3.0.50 ...

  9. 使用layui在规定的期限内选择日期

    这几天碰到了layui中的日期与时间这个,本以为很简单的,可是又一个需求是这样的,有两个日期选择框,第一个日期选择框要求最小日期不得小于当前日期,第二个日期选择框要求最小日期为第一个日期选择框的选中日 ...

  10. Dig out WeChat deleted chat messages on Android Phone

    As we know that WeChat will wipe deleted chat messages. That's why forensic guys could  not dig out ...