mongodb慢查询---之白夜追凶

业务背景

线上mongodb数据库查询数据超级慢,因为我们架构是分布式的架构,调用某个接口,接口查询mongodb数据的时候,非常卡,导致页面加载很慢。那我们就追追凶手。。。。。

查看服务器硬件资源使用率

分别使用top、free -m 、iostat -x 1查看服务器的CPU、内存、硬盘IO使用情况,得出结论如下:
内存使用情况和平常无异;磁盘io使用率正常;偏偏cpu使用率很高。得出结论cpu资源耗尽,导致产寻慢。

查找是什么原因导致cpu使用率居高不下:

借用其它博友的思维,导致cpu使用率居高不下的原因大概有如下几点:

CPU杀手1:全表扫描

全集合(表)扫描 COLLSCAN,当一个查询(或更新、删除)请求需要全表扫描时,是非常耗CPU资源的,所以当你在 system.profile 集合 或者 日志文件发现 COLLSCAN 关键字时,就得注意了,很可能就是这些查询吃掉了你的 CPU 资源;确认一下,如果这种请求比较频繁,最好是针对查询的字段建立索引来优化。

一个查询扫描了多少文档,可查看 system.profile 里的 docsExamined 的值,该值越大,请求CPU开销越大。

关键字:COLLSCAN、 docsExamined

CPU杀手2:不合理的索引

有的时候,请求即使查询走了索引,执行也很慢,通常是因为合理建立不太合理(或者是匹配的结果本身就很多,这样即使走索引,请求开销也不会优化很多)。

如下所示,假设某个集合的数据,x字段的取值很少(假设只有1、2),而y字段的取值很丰富。


{ x: 1, y: 1 }
{ x: 1, y: 2 }
{ x: 1, y: 3 }
......
{ x: 1, y: 100000} 
{ x: 2, y: 1 }
{ x: 2, y: 2 }
{ x: 2, y: 3 }
......
{ x: 1, y: 100000} 

要服务 {x: 1: y: 2} 这样的查询

db.createIndex( {x: 1} )         效果不好,因为x相同取值太多
db.createIndex( {x: 1, y: 1} )   效果不好,因为x相同取值太多
db.createIndex( {y: 1 } )        效果好,因为y相同取值很少
db.createIndex( {y: 1, x: 1 } )  效果好,因为y相同取值少

至于{y: 1} 与 {y: 1, x: 1} 的区别,可参考MongoDB索引原理 及 复合索引官方文档 自行理解。

一个走索引的查询,扫描了多少条索引,可查看 system.profile 里的 keysExamined 字段,该值越大,CPU 开销越大。

关键字:IXSCAN、keysExamined

CPU杀手3:大量数据排序

当查询请求里包含排序的时候,如果排序无法通过索引满足,MongoDB 会在内存李结果进行排序,而排序这个动作本身是非常耗 CPU 资源的,优化的方法仍然是建立索引,对经常需要排序的字段,建立索引。

当你在 system.profile 集合 或者 日志文件发现 SORT 关键字时,就可以考虑通过索引来优化排序。当请求包含排序阶段时, system.profile 里的 hasSortStage 字段会为 true。

关键字:SORT、hasSortStage

其他还有诸如建索引,aggregationv等操作也可能非常耗 CPU 资源,但本质上也是上述几种场景;建索引需要全表扫描,而vaggeregation 也是遍历、查询、更新、排序等动作的组合。

如何查看集合下的慢查询记录

先说如何查看本地实例是否开启了慢查询记录:

db.getProfilingStatus();
{ "was" : 2, "slowms" : 500 }

0:不记录;1:记录查询超过500毫秒的记录;2:记录所有查询记录。

设置级别和时间

#设置级别(这里要执行两次才能生效)
drug:PRIMARY> db.setProfilingLevel(2)
{ "was" : 1, "slowms" : 100, "ok" : 1 }
#设置级别和时间
db.setProfilingLevel(1,500);
{ "was" : 1, "slowms" : 500, "ok" : 1 }

需要说的是,在设置级别和时间的时候,只会在当前库下生效,如果想记录其它库的慢查询的话,需要进到对应的库里面进行设置。

#查看最近的慢查询日志记录
PRIMARY> db.system.profile.find().sort({$natural:-1})  
{ "ts" : ISODate("2013-05-14T08:13:37.098Z"), "op" : "insert", "ns" : "test.tickets", "millis" : 0, "client" : "127.0.0.1", "user" : "" }
{ "ts" : ISODate("2013-05-14T08:13:37.098Z"), "op" : "insert", "ns" : "test.tickets", "millis" : 0, "client" : "127.0.0.1", "user" : "" }
{ "ts" : ISODate("2013-05-14T08:13:37.098Z"), "op" : "insert", "ns" : "test.tickets", "millis" : 0, "client" : "127.0.0.1", "user" : "" }

ts:时间戳 
op: 操作类型
ns:执行操作的对象集合
millis:操作所花时间,毫秒 
client: 执行操作的客户端
user: 执行操作的mongodb连接用户

小编环境是读写分离的环境,所以要在从上查看日志还需要执行一句话:
SECONDARY> rs.slaveOk();










本文转自 xinsir999 51CTO博客,原文链接:http://blog.51cto.com/xinsir/2051629,如需转载请自行联系原作者
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值