MongoDB数据库Find查询长时间无响应

这次事故的起因是突然发现服务器有一个定时任务积累了超过50个进程没有退出。由于我们要定时做数据预处理,因此在服务器上部署了一个定时Python脚本,每10分钟执行一次。经过测试没有发现问题后就直接部署了,直到系统突然报警显示没有新数据流。实际上数据预处理的工作量并不大,不会涉及到耗时的计算。最终发现原因是脚本从Mongodb数据库中获取数据时,Mongodb长时间无响应,整个流程就卡住了。检查了MongoDB服务器,也没有发现CPU和内存异常。并且,MongoDB可以响应其它的查询操作。所以,这个事情就非常诡异了。

最不科学的解决方案:重启MongoDB服务器

由于要快速的解决问题,不然可能会影响到整个生产流水线。因此决定先重启MongoDB服务器恢复生产。具体操作可以参考MongoDB官方手册

./mongo 127.0.0.1:27017 -u "root" -p "root" --authenticationDatabase "admin"
use admin
db.shutdownServer()

或者直接在命令行执行以下命令:

./mongod --shutdown

随后重启服务:

./mongod --auth --port 27017 --bind_ip 0.0.0.0 --fork --dbpath /data/db --logpath /data/logs/mongod.log --logappend

问题复现

重启服务器是屡试不爽的灵丹妙药,但只能暂时缓解问题,不能从根本上解决问题。大家心里明白,问题会回来的。因为MongoDB查询没有响应,问题一定是发生在查询语句本身的。以下是脱敏以后的查询语句样例:

db.collection.find({_id: { $gt: ObjectId('5dfadc27302f6b2ebc84c700') }, uid: {$in:[1,2,3,4......9999]}}).sort({_id:1}).limit(100)

问题分析

如上例中的查询语句,我们要查询某条记录以后符合uid为1到9999的100条记录。由于我们是做数据预处理,每次都会记录前一次处理的最后一条记录的_id。所以新数据的量是可以控制的。在数据库中,我们已经对_id和uid分别做了普通索引和组合索引。所以速度慢一定不是没有加索引的问题。

重启服务器以后再次运行该语句能够正常查询。我们理想的工作流程是MongoDB会先使用_id条件把新数据筛选出来,然后通过uid筛选符合条件的数据。由于是新数据,所以数据量相比老数据是非常小的。但是一段时间以后,以上查询语句就不能工作了,长时间没有响应。因此我们推测,问题的原因可能是MongoDB冷热数据切换上。MongoDB并不是先作_id的筛选再做uid的筛选,而是先把符合uid条件的数据全部读到内存以后,对_id条件进行筛选。这样的话数据量会特别大(主要包含无用的历史数据),因此需要长时间的磁盘读写操作,导致查询长时间没有响应。

问题解决思路

基于以上猜测,我们评估的解决方案是利用MongoDB的聚合管道方案,先用$match通过_id把数据筛选一遍,然后在通过管道把符合uid条件的数据过滤出来。希望这样能够避免大量旧数据的IO操作。或者先使用_id条件把数据查询出来以后,在使用uid在查询结果上再查询一遍。

相关MongoDB操作

另外,以下是在分析这个问题过程中碰到的一些比较有用的MongoDB命令。

查看当前正在实现的MongoDB命令

该命令可以返回当前MongoDB中正在运行的命令。在本次问题中,我们看到Find查询一直在执行中,最长时间超过5000秒以上。

db.currentOp()

由于该命令会输出很多信息,如果需要快速定位运行时间长的查询操作,或者直接关闭的话,可以使用如下改良代码:

db.currentOP({"secs_running" : { "$gt" : 3 }}).inprog.forEach(
  function(op) {
      print([op.appName, op.opid, op.secs_running, op.command])
      //db.killOP(op.opid)
  }
)

以上指令只显示(删除)运行时间超过3秒的正在进行的查询。如果想要直接删除的话,只需要打开其中的注释即可。

关闭正在执行的MongoDB命令

通过operation id关闭正在执行的操作。operation id可以从currentOp命令中查到。

db.killOp(opid)

有些有价值的参考网站

以下是处理本问题过程中,提供相关帮助的网站:

  • https://blog.mlab.com/2014/02/mongodb-currentop-killop/
  • https://docs.mongodb.com/manual/aggregation/
  • https://stackoverflow.com/questions/37110869/combine-multiple-match-results-in-mongodb-aggregation-framework
Captain QR Code

扫码联系船长

发表回复

您的电子邮箱地址不会被公开。