监控Mongo慢查询

1. 使用mongostat监控MongoDB全局情况

 mongostat是mongdb自带的状态检测工具,在命令行下使用。它会间隔固定时间获取MongoDB的当前运行状态,并输出。
如果你发现数据库突然变慢或者有其他问题的话,你第一手的操作就考虑采用mongostat来查看mongo的状态。 mongostat --host localhost:27017 -uroot -p123456 --authenticationDatabase admin
参数说明:
host:指定IP地址和端口,也可以只写IP,然后使用--port参数指定端口号
-u: 如果开启了认证,则需要在其后填写用户名
-p: 密码
--authenticationDatabase:若开启了认证,则需要在此参数后填写认证库(注意是认证上述账号的数据库)

mongostat输出详解

insert/s : 官方解释是每秒插入数据库的对象数量,如果是slave,则数值前有*,则表示复制集操作
query/s : 每秒的查询操作次数
update/s : 每秒的更新操作次数
delete/s : 每秒的删除操作次数
getmore/s: 每秒查询cursor(游标)时的getmore操作数
command: 每秒执行的命令数,在主从系统中会显示两个值(例如 3|0),分表代表 本地|复制 命令
注: 一秒内执行的命令数比如批量插入,只认为是一条命令(所以意义应该不大)
dirty: 仅仅针对WiredTiger引擎,官网解释是脏数据字节的缓存百分比
used:仅仅针对WiredTiger引擎,官网解释是正在使用中的缓存百分比
flushes:
For WiredTiger引擎:指checkpoint的触发次数在一个轮询间隔期间
For MMAPv1 引擎:每秒执行fsync将数据写入硬盘的次数
注:一般都是0,间断性会是1, 通过计算两个1之间的间隔时间,可以大致了解多长时间flush一次。flush开销是很大的,
如果频繁的flush,可能就要找找原因了
vsize: 虚拟内存使用量,单位MB (这是 在mongostat 最后一次调用的总数据)
res: 物理内存使用量,单位MB (这是 在mongostat 最后一次调用的总数据)
注:这个和你用top看到的一样, vsize一般不会有大的变动, res会慢慢的上升,如果res经常突然下降,去查查是否有别的程序狂吃内存。 qr: 客户端等待从MongoDB实例读数据的队列长度
qw: 客户端等待从MongoDB实例写入数据的队列长度
ar: 执行读操作的活跃客户端数量
aw: 执行写操作的活客户端数量
注:如果这两个数值很大,那么就是DB被堵住了,DB的处理速度不及请求速度。看看是否有开销很大的慢查询。如果查询一切正常,确实是负载很大,就需要加机器了
netIn:MongoDB实例的网络进流量
netOut:MongoDB实例的网络出流量
注:此两项字段表名网络带宽压力,一般情况下,不会成为瓶颈
conn: 打开连接的总数,是qr,qw,ar,aw的总和
注:MongoDB为每一个连接创建一个线程,线程的创建与释放也会有开销,所以尽量要适当配置连接数的启动参数,
maxIncomingConnections,阿里工程师建议在5000以下,基本满足多数场景
set: 副本集的名称
repl: 节点的复制状态
M ---master
SEC ---secondary
REC ---recovering
UNK ---unknown
SLV ---slave
RTR ---mongs process("router')
ARB ---arbiter

2. 使用Profiling捕捉慢查询

类似于MySQL的slow log, mongodb可以监控所有慢的以及不慢的查询。这个工具就是Profiling,该工具在运行的实例上收集有关MongoDB的 写操作,游标,数据库命令等,可以在数据库级别开启该工具,也可以在实例级别开启。该工具会把收集到的所有都写入到system.profile集合中,该集合是一个capped collection。Profiling功能肯定是会影响效率的,但是不太严重,原因是他使用的是system.profile 来记录,而system.profile 是一个capped collection, 这种collection 在操作上有一些限制和特点,但是效率更高。

2.1 慢查询分析过程

1. 设置一个时间阀值,比如200ms
2. 在profiling中(system.profile)找到超过200ms的语句
3. 查看execStats,分析执行计划
4. 根据分析结果,决定是不是需要添加索引

2.2 Profiling基本操作

mongoshell(或者其他客户端比如mongochef等

#查看状态:级别和时间

PRIMARY> db.getProfilingStatus()

{ "was" : 1, "slowms" : 200 }

#查看级别
PRIMARY> db.getProfilingLevel() #级别说明:
0:关闭,不收集任何数据。
1:收集慢查询数据,默认是100毫秒。
2:收集所有数据 #设置级别
PRIMARY> db.setProfilingLevel(2)
{ "was" : 1, "slowms" : 100, "ok" : 1 } #这里返回的是上一次的设置 #设置级别和时间
PRIMARY> db.setProfilingLevel(1,200)
{ "was" : 2, "slowms" : 100, "ok" : 1 } #这里返回的是上一次的设置 #关闭Profiling
PRIMARY> db.setProfilingLevel(0)
{ "was" : 1, "slowms" : 200, "ok" : 1 } #这里返回的是上一次的设置 #清空system.profile或者修改大小
#关闭Profiling
PRIMARY> db.setProfilingLevel(0)
{ "was" : 0, "slowms" : 200, "ok" : 1 }
#删除system.profile集合
PRIMARY> db.system.profile.drop()
true
#创建一个新的system.profile集合 --- 4M
PRIMARY> db.createCollection( "system.profile", { capped: true, size:4000000 } )
{ "ok" : 1 }
#重新开启Profiling
PRIMARY> db.setProfilingLevel(1,200)
{ "was" : 0, "slowms" : 200, "ok" : 1 } #如果是复制集环境,要修改副本的system.profile的大小,必须把副本先从复制集中剔除,然后执行上述步骤,最后加入复制集。 #也可以MongoDB启动时,开启Profiling
mongod --profile=1 --slowms=200
#或者在配置文件里添加
profile = 1
slowms = 200

3. 日常使用的Profiling查询脚本

#返回最近的10条记录
db.system.profile.find().limit(10).sort({ts:-1}).pretty()
#返回所有的操作,除command类型的
db.system.profile.find({op: {$ne:'command'}}).pretty()
#返回特定集合
db.system.profile.find({ns:'mydb.test'}).pretty()
#返回大于5毫秒慢的操作
db.system.profile.find({millis:{$gt:5}}).pretty()
#从一个特定的时间范围内返回信息
db.system.profile.find(
{
ts : {
$gt : new ISODate("2015-10-18T03:00:00Z"),
$lt : new ISODate("2015-10-19T03:40:00Z")
}
}
).pretty()
#特定时间,限制用户,按照消耗时间排序
db.system.profile.find(
{
ts : {
$gt : newISODate("2015-10-12T03:00:00Z") ,
$lt : newISODate("2015-10-12T03:40:00Z")
}
},
{ user : 0 }
).sort( { millis : -1 } )
#查看最新的 Profile 记录:
db.system.profile.find().sort({$natural:-1}).limit(1)
# 显示5个最近的事件
show profile

4. 案例分析

4.1 获取慢查询

#下面的语句过滤几个大表,因为基本无法优化,需要开发改逻辑,所以做了排除,在输出方面只输出了个人认为重要的,方便分析迅速定位

db.system.profile.find({"ns":{"$not":{"$in":["F10data3.f10_4_4_1_gsgg_content", "F10data3.f10_5_1_1_gsyb_content"]}}}, {"ns":1,"op":1, "query":1,"keysExamined":1,"docsExamined":1,"numYield":1, "planSummary":1,"responseLength":1,"millis":1,"execStats":1}).limit(10).sort({ts:-1}).pretty()

#下面是一个超过200ms的查询语句
{
"op" : "query", #操作类型,有insert、query、update、remove、getmore、command
"ns" : "F10data3.f10_2_8_3_jgcc",
"query" : { #具体的查询语句 包括过滤条件,limit行数 排序字段
filter" : {
"jzrq" : {
"$gte" : ISODate("2017-03-31T16:00:00.000+0000"),
"$lte" : ISODate("2017-06-30T15:59:59.000+0000")
},
"jglxfldm" : 10.0
},
"ntoreturn" : 200.0,
"sort" : { #如果有排序 则显示排序的字段 这里是 RsId
"RsId" : 1.0
}
},
"keysExamined" : 0.0, #索引扫描数量 这里是全表扫描,没有用索引 所以是 0
"docsExamined" : 69608.0, #浏览的文档数 这里是全表扫描 所以是整个collection中的全部文档数
"numYield" : 546.0, #该操作为了使其他操作完成而放弃的次数。通常来说,当他们需要访问
还没有完全读入内存中的数据时,操作将放弃。这使得在MongoDB为了
放弃操作进行数据读取的同时,还有数据在内存中的其他操作可以完成。
"locks" : { #锁信息,R:全局读锁;W:全局写锁;r:特定数据库的读锁;w:特定数据库的写锁
"Global" : {
"acquireCount" : {
"r" : NumberLong(1094) #该操作获取一个全局级锁花费的时间。
}
},
"Database" : {
"acquireCount" : {
"r" : NumberLong(547)
}
},
"Collection" : {
"acquireCount" : {
"r" : NumberLong(547)
}
}
},
"nreturned" : 200.0, #返回的文档数量
"responseLength" : 57695.0, #返回字节长度,如果这个数字很大,考虑值返回所需字段
"millis" : 264.0, #消耗的时间(毫秒)
"planSummary" : "COLLSCAN, COLLSCAN", #执行概览 从这里看来 是全表扫描
"execStats" : { #详细的执行计划 这里先略过 后续可以用 explain来具体分析
},
"ts" : ISODate("2017-08-24T02:32:49.768+0000"), #命令执行的时间
"client" : "10.3.131.96", #访问的ip或者主机
"allUsers" : [ ],
"user" : ""
}

4.2 分析慢查询

 1. 如果发现 millis 值比较大,那么就需要作优化。
2. 如果docsExamined数很大,或者接近记录总数(文档数),那么可能没有用到索引查询,而是全表扫描。
3. 如果keysExamined数为0,也可能是没用索引。
4. 结合 planSummary 中的显示,上例中是 "COLLSCAN, COLLSCAN" 确认是全表扫描
5. 如果 keysExamined 值高于 nreturned 的值,说明数据库为了找到目标文档扫描了很多文档。这时可以考虑创建索引来提高效率。
6. 索引的键值选择可以根据 query 中的输出参考,上例中 filter:包含了 jzrq和jglxfldm 并且按照RsId排序,所以 我们的索引
索引可以这么建: db.f10_2_8_3_jgcc.ensureindex({jzrq:1,jglxfldm:1,RsId:1})

4.3 执行计划中的TYPE类型

COLLSCAN     #全表扫描                                                避免
IXSCAN #索引扫描 可以改进 选用更高效的索引
FETCH #根据索引去检索指定document
SHARD_MERGE #将各个分片返回数据进行merge 尽可能避免跨分片查询
SORT #表明在内存中进行了排序(与老版本的scanAndOrder:true一致) 排序要有index
LIMIT #使用limit限制返回数 要有限制 Limit+(Fetch+ixscan)最优
SKIP #使用skip进行跳过 避免不合理的skip
IDHACK #针对_id进行查询 推荐,_id 默认主键,查询速度快
SHARDING_FILTER #通过mongos对分片数据进行查询 SHARDING_FILTER+ixscan最优
COUNT #利用db.coll.explain().count()之类进行count运算
COUNTSCAN #count不使用Index进行count时的stage返回 避免 这种情况建议加索引
COUNT_SCAN #count使用了Index进行count时的stage返回 推荐
SUBPLA #未使用到索引的$or查询的stage返回 避免
TEXT #使用全文索引进行查询时候的stage返回
PROJECTION #限定返回字段时候stage的返回 选择需要的数据, 推荐PROJECTION+ixscan

监控Mongo慢查询的更多相关文章

  1. Mongo简单查询总结

    mongo 简单查询db.ansheng.findOne()//返回第一条匹配到的数据db.ansheng.findOne({"aaaaa":4})db.ansheng.find( ...

  2. (ArcGIS API For Silverlight )QueryTask 跨层查询,和监控完整的查询!

    (ArcGIS API For Silverlight )QueryTask 跨层查询,和监控完整的查询!     直接在源代码:     定义全局变量:    int  index=0; /// & ...

  3. Mongo 应用查询

    官网操作手册,基本就够用 https://docs.mongodb.com/manual/ 下面是个分组查询的例子,项目中用到然后查了个例子,自己理解了下,觉得很好很强大. https://blog. ...

  4. grafana使用Prometheus数据源监控mongo数据库

    数据库改用mongo后,监控需求就需要整合进grafana里,由于一直在坚持docker化部署,那么此次也不例外. 1. 安装Prometheus: What is Prometheus? Prome ...

  5. Zabbix监控Mongo

    安装Zabbix-agent # groupadd zabbix # useradd -g zabbix zabbix # yum -y install gcc mysql-community-dev ...

  6. Mongo常用查询语法

    一.查询 find方法 db.collection_name.find(); 查询所有的结果: select * from users; db.users.find(); 指定返回那些列(键): se ...

  7. SqlProfilter监控指定数据库数据表——监控linq组合查询生成的sql

    1.例子 实际测试环境中往往很多测试都在调用数据库,那么如何使用SqlProfilter监控筛选到自己想要监看的数据库对应的表有关linq生成的sql时候就需要做如下设置了 ........... u ...

  8. mongo 慢查询配置

    我是分片部署,所以慢查询相关的配置是在启动片服务上. 执行查询命令,是在share的primary 上. 1. mongodb慢查询   配置 慢查询数据主要存储在 local库的system.pro ...

  9. Sentry 监控 - Search 搜索查询实战

    系列 1 分钟快速使用 Docker 上手最新版 Sentry-CLI - 创建版本 快速使用 Docker 上手 Sentry-CLI - 30 秒上手 Source Maps Sentry For ...

随机推荐

  1. ASP.NET MVC 中的IResolver<T> 接口

    在ASP.NET MVC 的源码一些实体对象(比如 ControllerBuilder,ControllerFactory, Filters, ViewEngines 等)不再直接通过关键字new来创 ...

  2. Java基础学习-代码块

    /*代码块: * 用{}修饰的代码 * 1.局部代码块:控制变量,存在方法中,控制变量的生命周期(作用域) * 2.构造代码块:提取构造方法中的共性,每次创建对象,都会执行,并且在构造方法执行之前执行 ...

  3. 记录个人数组、字符串自己常忘记的方法,以及ES常用处理方式

    记录自己在工作中,时不时使用,每次都要去查一下的基础方法.以及ES6经常使用的方法 一.Array 1.concat 合并数组 2.shift 获取数组第一个元素 unshift 向数组首位添加一个元 ...

  4. flex 弹性布局的大坑!!

    如果父元素设置 display:flex,那么其中的子元素会被当成行内元素对待,即会忽略其宽度 这在设置背景图时要特别特别注意!!!!

  5. 转: Photon 3.4 Changed Logs ..

    最新版了.修了很多bug. Photon 3.4 http://doc.exitgames.com/en/onpremise/current/reference/version-history/pho ...

  6. Comparing Xamarin and Delphi XE5 to Xcode for Cross Platform Mobile App Development

    Comparing Xamarin and Delphi XE5 to Xcode for Cross Platform Mobile App Development If you are consi ...

  7. 人生苦短之我用Python篇(基础)

    Python简介 Python,是一种面向对象的解释型计算机程序设计语言,由荷兰人Guido van Rossum于1989年发明,第一个公开发行版发行于1991年. Python是纯粹的自由软件, ...

  8. linux ls-al 指令详解

    ls -al 具体说明请自行找男人(man 中了鸟哥的毒 =.=).

  9. PHPCMS V9调用父栏目 顶级父栏目的代码

    一.调用父栏目 首先是列表页和二级栏目页list.html {$CATEGORYS[$top_parentid][catname]} //顶级父栏目名称 {$CATEGORYS[$CAT[parent ...

  10. DbEntry 简单实现

    在着手编码之前首先安装DbEntry DbEntry.Net.4.1.Setup.zip 在建立类库时选择 DbEntryClassLibrary 如图 DbEntryClassLibrary1 中建 ...