Mongodb查询缓慢问题处理

早上突然收到配置中心服务告警,Http check出现问题,赶紧访问下服务看看是否Cabot监控系统的误报,发现服务时而正常,时而出现500内部服务器错误,赶紧排查问题。

1、问题定位

首先看看当前配置中心的总体上报statsd请求量是否出现飙升:

 

 

确实负载上来了,先看看应用服务器有没有报错,再确定是应用服务问题,还是数据库的问题:

tail -f logs/log.log

 

 

 

应用服务器出现了一堆”no reachable servers”的报错,那么说明要么是数据库挂掉了,要么是出于各种原因无响应。

那就登录到几台数据库服务器上面,看看Mongodb进程,发现进程都还在的,那么再看看master上面的数据库日志:

ps -ef | grep mongo

发现日志里面出现了一堆的too many open connections: 819这样的错误。

Mongodb默认的最大连接数是819,当连接数到达819后,就无法增加,那么新的请求就无法再连接上去了。

OK,感觉问题很清晰了,时而正常时而出现500错误的原因,是因为请求量上来以后,旧的数据库连接还没有释放,新的又创建了,导致触碰到Mongodb的最大连接数限制。

2、问题处理

那么做两个方面的调整:

1)Mongodb数据库的最大连接数改大到20000(Mongodb允许的最大的连接数是20000),避免再次出现最大连接数限制:

vim mongodb.conf
添加:
maxConns=20000

2)在应用里面设置poolLimit,让mgo尽量多地复用现存的数据库连接,不要再无限制地增加Mongodb连接了,先设置为单个服务器最大200个连接。

Mgo里面,由于我的Mongodb集群是复制集,无法直接在mongodb的数据库连接uri里面像这样添加:

mongodb://xx:xx@10.xx.xx.xx:27017/index-config?maxPoolSize=10

因为我的复制集连接是这样的:

mongodb://xx:xx@172.19.8.76:27017,172.19.8.77:27017,172.19.9.76:27017,172.19.9.77:27017/index-config?replicaSet=configrs

查下Mgo的官方文档,看看怎么设置最大连接池数量限制,找到一项SetPoolLimit:

http://godoc.org/gopkg.in/mgo.v2#Session.SetPoolLimit

SetPoolLimit sets the maximum number of sockets in use in a single server before this session will block waiting for a socket to be available. The default limit is 4096.

这里面写道,默认情况下,Mgo的最大连接数限制是4096,那么当然高过Mongodb默认的最大819限制了,添加限制,先设置为200:

Session.SetPoolLimit(200)

都改好,重启服务,感觉又要解决一个问题了~

3、出现新的问题

打包、重新部署服务,重新刷新服务,恢复正常。

然而,没过1分钟,又收到告警邮件,看下服务,失败量比之前还多:

 

纳尼?!难道处理问题的姿势不对?赶紧看看Mongodb 的master服务日志:

tail -f logs/mongodb.log

4、继续排查问题

看到日志里面频繁出现大量的Debug 跟Info级别的COMMAND,那么估计是我的日志级别开的不对,先调整下日志级别,关闭Debug级别的各项输出:

/usr/local/mongodb/bin/mongo mongodb://xxx:xxx@172.19.8.76:27017,172.19.8.77:27017,172.19.9.76:27017,172.19.9.77:27017/admin?replicaSet=configrs

 

db.setLogLevel(0)

Mongodb里面,LogLevel分为0-5,其中0表示仅开启Info级别日志,1-5表示Debug级别日志的打印数量,数字越大,打印的越多。

继续查问题。先看看服务器状态:

top下看看CPU、负载、内存使用率,这不看不知道,一看吓了一大跳,CPU使用率居然高达96%,基本都被Mongodb占用了,而服务器负载高达几百,内存使用率却还剩下许多(紧急地处理问题,没有截图,宝贵的Top结果已经找不到了),说明当前的服务瓶颈出现在CPU。

首先分析是不是频繁的IO导致服务器CPU消耗过大:

iostat

 

看到IO并不是CPU高占比的原因。

重新分析一遍问题,先前是时而正常时而有问题,现在的问题比之前更频繁得多,那么跟第一次修复的连接数肯定有一定关系。

用mongostat看看服务:

/usr/local/mongodb/bin/mongostat --username=xxx --password=xxx --authenticationDatabase=admin

访问情况基本是正常的。

进入Mongodb看看数据库连接数:

db.serverStatus().connections

连接数也正常。

再看看网络状态,Establish状态的是否很多:

netstat -anp | grep ESTABLISHED

看到应用服务器确实是有正在处理的连接请求,那么结合上下文,判断应该是由于限制了最大单台机器200个连接,而申请的速度比释放的速度快,导致了大量请求阻塞在连接释放的地方,导致超时报错。

开启数据库慢查询看看:

 

db.setProfilingLevel(1,200)

db.system.profile.find()

打印出大量的慢查询请求,集中在直接查询用户信息的user_config集合上面。

配置中心的请求分为两种,一种是全局统一数据,通过用户的不同APP版本,IMEI等信息范围相应数据的灰度版本控制,或者APP配置信息等数据,一种是用户相关,每个用户各自读写信息的数据。

前一种数据使用的是缓存机制,用户的请求命中的基本都是缓存;第二种数据太多,无法采用缓存,因此直接读写DB,当前来说,也即是user_config集合。

大量慢查询请求出现在user_config上,说明查询过慢,但IO明显不是瓶颈。

这时候我突然想到原本计划上线时候需要做,但还没做的索引,赶紧补充上去:

db.user_config.ensureIndex({"portal_id":1})

因为每次用户查询的索引值都是portal_id,所以仅需要对portal_id做索引。

为防止自己忘记,在代码里面也添加自动索引的内容:

func init() {
    db := mongodb.ConnectMongo()
    defer db.Session.Close()
    index := mgo.Index{
        Key: []string{"portal_id"},
    }
    db.C(CollectionUserConfig).EnsureIndex(index)
}

Mongodb里面,如果不添加索引,对Mongodb数据表进行查询操作的时候,需要把数据都加载到内存。当数据的数量达到几十万乃至上百万的时候,这样的加载过程会对系统造成较大的冲击,并影响到其他请求的处理过程。

用户表当前的记录数量已经达到45万,每次都加载这么多数据,查询起来肯定是非常慢的。建立索引以后对索引字段进行查询时,仅会加载索引数据,能极大地提高查询速度。

添加索引以后,很快收到了服务恢复的邮件:

配置中心 RECOVERY.

再访问服务,正常了!

本文引用:https://chen-kaka.github.io/problems/2017/08/21/Mongodb%E6%9F%A5%E8%AF%A2%E7%BC%93%E6%85%A2%E9%97%AE%E9%A2%98%E5%A4%84%E7%90%86/

posted @ 2018-01-04 11:15  单先生  阅读(31988)  评论(0编辑  收藏  举报