在mongos上无法通过db.setProfilingLevel开启Profiler查慢查询,因mongos仅路由不执行查询;需在各shard节点单独启用Profiler,并结合mongos的--slowms日志、sh.status()、currentOp及系统指标综合诊断。

mongos 上怎么开 Profiler 查慢查询
mongos 本身不执行查询,只做路由,所以直接在 mongos 上开启 db.setProfilingLevel() 没用——它不会记录任何实际的查询耗时。你看到的全是“no profiling information available”或者空结果。
真正要查慢查询,得去后端 shard 节点(即每个 mongod 实例)上单独开 Profiler。但 mongos 的日志里能帮你定位:哪些查询被路由到了哪个 shard、用了多久、是否触发了跨 shard 查询(scatter-gather),这些信息藏在 mongos 的 slowms 日志里。
- 确保 mongos 启动时加了
--slowms 100(或更低值),否则默认 100ms 以上的才记日志 - 查看 mongos 日志路径(通常是
/var/log/mongodb/mongos.log),用grep "command" | grep "secs"快速筛出耗时操作 - 注意日志里带
"planSummary":"SHARD_MERGE"的条目——这是跨分片聚合,大概率是性能瓶颈源头
在 shard 节点上启用 Profiler 并过滤慢操作
每个 shard 是独立的 mongod 进程,Profiler 必须在每个节点上分别开启。别漏掉 config server(虽然它不存业务数据,但某些元数据操作慢也会影响整体)。
进到某个 shard 的 mongo shell(连本地 localhost:27018 这类端口),执行:
use your_db_name
db.setProfilingLevel(1, { slowms: 50 })
这里 slowms: 50 比默认 100 更敏感,因为分片环境下单次查询可能被拆解,小延迟叠加起来就成大问题。
- Profile 数据存在
system.profile集合里,只保留最近 1MB 或最近 30 分钟(取决于配置),别等出问题了再开 -
db.system.profile.find({ millis: { $gt: 100 } }).sort({ ts: -1 }).limit(5)是最常用的排查命令 - 注意
system.profile不会自动创建索引,加db.system.profile.createIndex({ ts: -1 })能加速查询
为什么 profiler 日志和 mongos 日志对不上
常见现象:mongos 日志里显示某条查询花了 320ms,但去对应 shard 的 system.profile 里找不到这条记录,或者只看到几个 5ms 的子操作。
根本原因是:mongos 日志记录的是“从收到请求到返回响应”的总耗时(含网络、协调、合并结果时间),而 shard 的 profiler 只记录它自己执行的那一部分。如果查询涉及多个 shard,每个只记自己的片段,加起来也不等于 mongos 总耗时。
- 跨 shard 的
$lookup或aggregate会触发多次远程调用,mongos 日志里可能只有一行,但背后有 N 次 shard 间通信 - shard 节点若启用了
enableLocalhostAuthBypass,且没配好 keyfile,会导致 mongos 和 shard 之间认证延迟,这种延迟 profiler 完全不记录 - 用
db.currentOp({ secs_running: { $gt: 2 } })在 shard 上实时抓长事务,比 profiler 更及时
真正有用的慢查询归因技巧
光看耗时没用,得知道慢在哪一环。分片环境里,90% 的“慢查询”其实不是查询本身慢,而是路由、锁、IO 或内存分配出了问题。
优先检查这三件事:
- 运行
sh.status()看 chunk 分布是否倾斜——某个 shard 承载了 80% 的 chunk,自然容易慢 - 在 mongos 上执行
db.adminCommand({ currentOp: 1, secs_running: { $gt: 3 } }),重点看waitingForLock和active: true的操作 - 查 Linux 层:每个 shard 节点上跑
iostat -x 1 3,如果%util持续 >90 或await>30ms,说明磁盘扛不住,profiler 再准也没用
Profiler 是显微镜,不是诊断仪。它告诉你“这个操作慢”,但不会告诉你“为什么慢”——那得靠 sh.status()、currentOp、系统指标三者交叉验证。漏掉任意一个,都可能把 IO 瓶颈误判成查询写法问题。










