MongoDB使用profile分析慢查询的步骤


Posted in MongoDB onApril 30, 2021

      在MongoDB中,如果发生了慢查询,我们如何得到这些慢查询的语句,并优化呢?今天来看这块儿的一些心得。

01 如何收集慢查询?

    在MongoDB中,通常可以开启profile来收集慢日志,查看当前profile状态的语句如下:

test1:PRIMARY> db.getProfilingStatus()
{
        "was" : 2,
        "slowms" : 0,
        "sampleRate" : 1,
        "$gleStats" : {
                "lastOpTime" : Timestamp(0, 0),
                "electionId" : ObjectId("7fffffff0000000000000005")
        },
        "lastCommittedOpTime" : Timestamp(1619186976, 2),
        "$configServerState" : {
                "opTime" : {
                        "ts" : Timestamp(1619186976, 1),
                        "t" : NumberLong(2)
                }
        },
        "$clusterTime" : {
                "clusterTime" : Timestamp(1619186976, 2),
                "signature" : {
                        "hash" : BinData(0,"zvwFpgc0KFxieMpj7mBPdrOnonI="),
                        "keyId" : NumberLong("6904838687771590657")
                }
        },
        "operationTime" : Timestamp(1619186976, 2)
}

这里我们可以看到2个关键参数,分别是was和slowms,其中:

was=0,代表不记录任何的语句;

was=1,代表记录执行时间超过slowms的语句

was=2,代表记录所有的语句

slowms代表语句的阈值,单位是ms

上图中的结果代表我们的实例会收集所有的查询语句。profile收集的查询语句结果存放在admin数据库中的system.profile集合中,可以通过下面的方法进行访问:

test1:PRIMARY> use admin
switched to db admin

test1:PRIMARY> db.system.profile.find({'op':'query'},{'op':1,'ns':1,'millis':1,'ts':1})
{ "op" : "query", "ns" : "admin.system.users", "millis" : 0, "ts" : ISODate("2020-08-27T07:22:14.815Z") }
{ "op" : "query", "ns" : "admin.system.users", "millis" : 0, "ts" : ISODate("2020-08-27T07:22:15.139Z") }
{ "op" : "query", "ns" : "admin.system.users", "millis" : 0, "ts" : ISODate("2020-08-27T07:22:15.141Z") }
{ "op" : "query", "ns" : "admin.system.users", "millis" : 0, "ts" : ISODate("2020-08-27T07:22:15.239Z") }
{ "op" : "query", "ns" : "admin.system.version", "millis" : 0, "ts" : ISODate("2020-08-27T07:22:16.155Z") }
{ "op" : "query", "ns" : "admin.system.version", "millis" : 0, "ts" : ISODate("2020-08-27T07:22:16.192Z") }
{ "op" : "query", "ns" : "admin.system.users", "millis" : 0, "ts" : ISODate("2020-08-27T07:22:16.225Z") }
{ "op" : "query", "ns" : "admin.system.users", "millis" : 0, "ts" : ISODate("2020-08-27T07:22:16.273Z") }
{ "op" : "query", "ns" : "admin.system.version", "millis" : 0, "ts" : ISODate("2020-08-27T07:22:16.276Z") }

02 system.profile慢查询集合分析

   admin数据库中的system.profile是一个固定集合,保存着超过设置的慢查询的结果。我们来看里面的一条慢查询。

    利用下面的方法,来拿到一条数据,并对其中的关键字段进行注释说明:

test1:PRIMARY> db.system.profile.findOne({'op':'query'})
{
        "op" : "query",  # 操作类型
        "ns" : "admin.system.users",  # 命名空间
        "command" : {
                "find" : "system.users",
                "filter" : {
                        "_id" : "admin.root"  # 过滤的字段
                },
                "limit" : 1,
                "singleBatch" : true,
                "lsid" : {
                        "id" : UUID("a6034f5e-77c1-4b19-9669-60e1253edf4b")
                },
                "$readPreference" : {
                        "mode" : "secondaryPreferred"
                },
                "$db" : "admin"
        },
        "keysExamined" : 1,   # 扫描的索引数
        "docsExamined" : 1,   # 扫描的行数
        "cursorExhausted" : true,  
        "numYield" : 0,
        "nreturned" : 1,   # 返回的值的行数
        "locks" : {
                xxxx   #  锁信息
        },
        "flowControl" : {

        },
        "storage" : {
        },
        "responseLength" : 647,
        "protocol" : "op_query",
        "millis" : 0,    # 这个查询的执行时间,因为我们设置的profilestatus是0,因此所有操作都被记录了。
        "planSummary" : "IDHACK",  # 针对_id进行查询
        "execStats" : {   # 查询执行状态
                "stage" : "IDHACK",
                "nReturned" : 1,
                "executionTimeMillisEstimate" : 0,
                "works" : 2,
                "advanced" : 1,
                "needTime" : 0,
                "needYield" : 0,
                "saveState" : 0,
                "restoreState" : 0,
                "isEOF" : 1,
                "keysExamined" : 1,
                "docsExamined" : 1
        },
        "ts" : ISODate("2020-08-27T07:22:14.815Z"),
        "client" : "xx.xx.xx.xx",  # 查询的客户端IP地址
        "allUsers" : [   #  所有的用户信息
                {
                        "user" : "root",
                        "db" : "admin"
                }
        ],
        "user" : "root@admin"   # 使用的用户信息
}

03 慢查询分析利器---explain

   通常情况下,我们可以使用MongoDB的explain语法来分析一个语句的查询性能,包含是否用到索引、扫描行数等信息,explain语法的基本用法:

后置写法
db.system.profile.find({'op':'query'}).explain()
前置写法
db.system.profile.explain().find({'op':'query'})

其中,explain可以放在查询语句的后面或者前面,当然find语法也可以是update、remove、insert

explain语法的输出分为3种不同的详细程度,分别如下:

三种清晰度模式,清晰度越高,则输出的信息越全,默认情况下是queryPlanner:

1、queryPlanner模式(默认)
db.products.explain().count( { quantity: { $gt: 50 } } )

2、executionStats模式
db.products.explain("executionStats").count( { quantity: { $gt: 50 } } )

3、allPlansExecution模式
db.products.explain("allPlansExecution").count( { quantity: { $gt: 50 } } )

其中,allPlansExecution模式输出的信息最多。

下面是一个explain语法的输出内容,查询的SQL如下:

db.getCollection('files').find(
{"cTime":{
           "$gte":ISODate("2021-04-18"),
           "$lt":ISODate("2021-04-19")
       }}).limit(1000).explain("allPlansExecution")

输出的结果如下:

{
        "queryPlanner" : {   # 代表查询的执行计划
                "plannerVersion" : 1,   # 版本号
                "namespace" : "fs.files",   # 查询的命名空间,也就是集合名称
                "indexFilterSet" : false,   # 是否使用了索引过滤,注意,它并不能判定是否使用了索引
                "parsedQuery" : {    # 查询语法解析树
                        "$and" : [
                                {
                                        "cTime" : {
                                                "$lt" : ISODate("2021-04-19T00:00:00Z")
                                        }
                                },
                                {
                                        "cTime" : {
                                                "$gte" : ISODate("2021-04-18T00:00:00Z")
                                        }
                                }
                        ]
                },
                "winningPlan" : {    # 最终选择的查询计划
                        "stage" : "LIMIT",   # 查询的阶段,很重要,下面详细介绍
                        "limitAmount" : 1000,   # 查询结果的limit值
                        "inputStage" : {
                                "stage" : "FETCH",
                                "inputStage" : {
                                        "stage" : "IXSCAN",  # 代表索引扫描
                                        "keyPattern" : {
                                                "cTime" : 1
                                        },
                                        "indexName" : "cTime_1",  #  索引名称
                                        "isMultiKey" : false,    # 下面4个字段都是索引类型分析
                                        "isUnique" : false,
                                        "isSparse" : false,
                                        "isPartial" : false,
                                        "indexVersion" : 1,
                                        "direction" : "forward",
                                        "indexBounds" : {
                                                "cTime" : [
                                                        "[new Date(1618704000000), new Date(1618790400000))"
                                                ]
                                        }
                                }
                        }
                },
                "rejectedPlans" : [ ]  # 候选的没被选中的查询计划
        },
        "serverInfo" : {
                "host" : "xxxx",
                "port" : 24999,
                "version" : "3.2.8",
                "gitVersion" : "ed70e33130c977bda0024c125b56d159573dbaf0"
        },
        "ok" : 1
}

首先解释下stage的几个阶段:

  1. COLLSCAN---全表扫描
  2. IXSCAN---索引扫描
  3. FETCH---根据索引去检索文档
  4. SHARD_MERGE---合并分片结果
  5. IDHACK---针对id进行查询
  6. LIMIT---执行limit

了解了这些stage的阶段之后,我们可以看到,一个查询的过程是一层一层解析的,所以可以看到,stage这个字段有嵌套的情况。winningPlan中的执行计划也是按照一层一层的顺序去执行:

1、先执行最内层的索引扫描(IXSCAN);

2、再执行外面的FETCH,根据索引去拿文档

3、执行最后一步的limit,取指定数目个结果返回给客户端

以上就是MongoDB profile分析慢查询的示例的详细内容,更多关于MongoDB profile分析慢查询的资料请关注三水点靠木其它相关文章!

MongoDB 相关文章推荐
MongoDB数据库常用的10条操作命令
Jun 18 MongoDB
MongoDB安装使用并实现Python操作数据库
Jun 28 MongoDB
常用的MongoDB查询语句的示例代码
Jul 25 MongoDB
MongoDB连接数据库并创建数据等使用方法
Nov 27 MongoDB
一次线上mongo慢查询问题排查处理记录
Mar 18 MongoDB
MongoDB数据库部署环境准备及使用介绍
Mar 21 MongoDB
MongoDB误操作后使用oplog恢复数据
Apr 11 MongoDB
MongoDB支持的数据类型
Apr 11 MongoDB
NoSQL优缺点与MongoDB数据库简介
Jun 05 MongoDB
MongoDB balancer的使用详解
Apr 30 #MongoDB
MongoDB数据库的安装步骤
Jun 18 #MongoDB
MongoDB数据库常用的10条操作命令
Jun 18 #MongoDB
MongoDB 常用的crud操作语句
Jun 20 #MongoDB
MongoDB orm框架的注意事项及简单使用
Jun 20 #MongoDB
详解MongoDB的条件查询和排序
Jun 23 #MongoDB
SpringBoot整合MongoDB的实现步骤
Jun 23 #MongoDB
You might like
PHP array操作10个小技巧分享
2011/06/23 PHP
php数组函数序列之array_pop() - 删除数组中的最后一个元素
2011/11/07 PHP
PHP标准类(stdclass)用法示例
2016/09/28 PHP
TP - 比RBAC更好的权限认证方式(Auth类认证)
2021/03/09 PHP
javascript 获取表单file全路径
2009/12/31 Javascript
jQuery学习笔记之Helloworld
2010/12/22 Javascript
优化Jquery,提升网页加载速度
2013/11/14 Javascript
用javascript关闭本窗口不弹出询问框的方法
2014/09/12 Javascript
javascript正则表达式之search()用法实例
2015/01/19 Javascript
angularJS中router的使用指南
2015/02/09 Javascript
AngularJS实现使用路由切换视图的方法
2017/01/24 Javascript
NodeJS配置HTTPS服务实例分享
2017/02/19 NodeJs
vue2利用Bus.js如何实现非父子组件通信详解
2017/08/25 Javascript
微信小程序自定义头部导航栏和导航栏背景图片 navigationStyle问题
2019/07/26 Javascript
linux系统使用python监测系统负载脚本分享
2014/01/15 Python
Python 抓取动态网页内容方案详解
2014/12/25 Python
Python实现剪刀石头布小游戏(与电脑对战)
2019/12/31 Python
python3.8动态人脸识别的实现示例
2020/09/21 Python
HTML5有哪些新特征
2015/12/01 HTML / CSS
俄罗斯玩具、儿童用品、儿童服装和鞋子网上商店:MyToys.ru
2019/10/14 全球购物
几道Web/Ajax的面试题
2016/11/05 面试题
下面代码从性能上考虑,有什么问题
2015/04/03 面试题
大一学生的职业生涯规划书范文
2014/01/19 职场文书
关于廉洁的广播稿
2014/01/30 职场文书
幼师求职信
2014/06/23 职场文书
大专应届毕业生求职信
2014/07/15 职场文书
公司委托书怎么写
2014/08/02 职场文书
安全保证书
2015/01/16 职场文书
教师党员个人总结
2015/02/10 职场文书
学生逃课检讨书
2015/02/17 职场文书
员工安全责任协议书
2016/03/22 职场文书
导游词之太行山青龙峡
2020/01/14 职场文书
php png失真的原因及解决办法
2021/11/17 PHP
动画《平凡职业成就世界最强》宣布制作OVA
2022/04/01 日漫
HTML实现仿Windows桌面主题特效的实现
2022/06/28 HTML / CSS