Ruby脚本远程执行MongoDB查询耗时波动大的原因排查
为什么我的MongoDB查询时快时慢?
让咱们一步步拆解这个问题,结合你的代码和MongoDB的特性,看看哪些因素可能导致这种忽快忽慢的情况:
首先先贴出你的代码方便参考:
script.rb
require '/home/ubuntu/praneetha/check_mongo/publisher_models.rb' $stdout.sync = true begin count = 1 user_id = '53ce011f9937d923a4000001' while(true) puts "count: #{count}" t1 = Time.now.utc puts "Time.now is ========= #{t1}" p = Puser.find_by(:puid => user_id) puts "puser is ====#{p.class}" t2 = Time.now.utc t3 = t2-t1 puts "time taken =====> #{t3}" if t3>2 puts "I have an error at time #{t1} and total time is #{t3} " end count = count + 1 sleep(300) end end
publisher_models.rb
require 'mongoid' require 'em-synchrony' class Puser include Mongoid::Document field :email, type: String #custom user id for the user. This should be indexed at the time of creation field :puid, type: String end
最可能的原因:索引缺失或未生效
你的模型注释里提到puid字段“应该在创建时建立索引”,但实际可能并没有创建索引。这是导致查询时快时慢的头号嫌疑人:
- 如果没有索引,MongoDB执行
find_by(:puid => user_id)时会做全集合扫描(遍历所有文档找匹配项)。如果集合数据量很大,全表扫描的耗时会极不稳定:有时候刚好目标文档在内存缓存里,查询就快;如果文档被换出到磁盘,或者服务器正忙,就会慢到离谱(比如你遇到的15分钟)。 - 排查方法:登录到MongoDB shell,执行
db.pusers.getIndexes(),检查输出里是否有puid: 1的索引。如果没有,立刻创建:
创建索引后,再观察查询耗时应该会稳定在毫秒级。db.pusers.createIndex({ puid: 1 })
服务器资源波动或负载过高
你的MongoDB是部署在远程服务器上的,服务器的CPU、内存、磁盘IO资源如果被其他任务占用,会直接拖慢查询速度:
- 比如服务器正在执行大查询、数据备份、批量写入操作,或者其他应用占用了大量CPU/内存,你的查询就会被抢占资源,导致耗时陡增。
- 排查方法:
- 用
top、iostat等工具监控服务器的CPU、内存、磁盘读写使用率,看查询变慢时是否对应资源峰值。 - 查看MongoDB的日志文件(通常是
/var/log/mongodb/mongod.log),看有没有慢查询记录、锁等待或者资源不足的警告。
- 用
网络链路问题
因为是远程连接,网络波动会直接影响查询耗时:
- 比如网络拥堵、丢包、延迟突然升高,都会导致你的请求从本地到Mongo服务器的传输时间变长,甚至需要重传数据包,最终表现为查询耗时暴涨。
- 排查方法:用
ping、traceroute工具测试本地到Mongo服务器的网络延迟,观察是否有明显的波动或丢包情况。
数据缓存与内存压力
MongoDB会把常用数据缓存到内存中,如果服务器内存不足,缓存的热数据会被换出到磁盘:
- 你的脚本是循环查询同一个
user_id,如果这个文档刚好在内存缓存里,查询就快;如果缓存被其他数据挤掉,需要从磁盘读取,就会慢很多。 - 排查方法:查看MongoDB的
serverStatus输出(执行db.serverStatus()),关注mem字段里的缓存命中率,如果命中率过低,说明服务器内存不足,需要扩容或者优化数据存储。
集合碎片问题
如果pusers集合有大量的删除、更新操作,会导致集合产生碎片:
- 碎片会让MongoDB在查询时需要遍历更多的磁盘块,耗时变得不稳定。
- 排查方法:执行
db.pusers.stats()查看storageSize和dataSize的差值,如果差值很大,说明碎片较多。可以用db.runCommand({ compact: 'pusers' })整理碎片(注意:compact操作会锁集合,建议在低峰期执行)。
锁等待问题
不同版本的MongoDB锁粒度不同(比如旧版MMAPv1是库级锁,新版WiredTiger是文档级锁):
- 如果有大量的写操作(插入、更新、删除)在
pusers集合上执行,你的查询可能需要等待锁释放,导致耗时变长。 - 排查方法:执行
db.currentOp()查看当前正在运行的操作,看有没有长时间等待锁的查询。
连接池或连接复用问题
虽然你的脚本是循环查询,但如果Mongoid的连接池配置不合理,可能会导致每次查询都重新建立连接:
- 重新建立TCP连接的耗时会远高于复用已有连接,偶尔的连接超时或重建就会让查询耗时暴涨。
- 排查方法:检查Mongoid的连接配置(通常在
mongoid.yml里),确保连接池的max_pool_size、timeout等参数设置合理,避免连接频繁断开重连。
先从检查索引开始排查吧,这是最容易解决也最可能的原因。如果索引没问题,再依次排查服务器资源、网络、缓存这些因素。
内容的提问来源于stack exchange,提问作者Praneetha
相关产品推荐
相关产品推荐

