跳到主要内容

MongoDB 慢查询分析

基于 MongoDB profiler 自动识别慢查询并给出索引优化建议,入口在「资源监控 → MongoDB」的慢查询页签。

前提
  • 被诊断的 MongoDB 需在数据源页添加并启用,且用途勾选慢查询诊断
  • 多个启用诊断的实例(如分片集群的多个分片)会自动聚合分析,无需额外配置。

工作原理

  1. 开启 profiler:平台检测到目标库 profiling 未开启时,会自动执行 db.runCommand({profile:1, slowms:100}) 打开(这是平台对被监控库唯一的配置写入;不会创建索引、也不会写入业务数据)。若监控账号权限不足,日志中会出现 Permission denied,此时需要手工开启,见下。
  2. 采集:每 10 分钟从 MongoDB system.profile 拉取一次,只看最近 120 分钟内的记录,单次最多取 2000 条。
  3. 筛选:只保留扫描文档数 ≥ 100000 的记录。注意「执行慢但扫描少」的查询不会被记录。
  4. 分组:按 query shape(忽略具体参数值的查询模板)聚合,同一指纹累计出现 ≥ 50 次才写入。每组显示累计次数、平均 / 最长耗时、扫描与返回文档数、典型样例。
  5. 存储:结果只写入平台自有的 ops-mongo,保留 7 天。
  6. 索引建议:结合查询字段组合与现有索引,给出可创建的复合索引,支持一键创建(后台调用 createIndex——这是唯一会往被监控库写入的操作,需要你主动点击)。
抓不到慢查询?先对照这四个条件

「慢」本身不是记录条件,真正的门槛是**「扫得多 + 跑得频繁」**,四个条件必须同时满足:

条件默认值可调环境变量
执行时长> 100 msENV_GATEWAY_PROFILE_SLOW_MS
扫描文档数≥ 100000ENV_GATEWAY_DOCS_EXAMINED
同指纹出现次数≥ 50 次(最近 120 分钟内)
采集周期每 10 分钟一次ENV_GATEWAY_CAPTURE_INTERVAL_MS

只造一条 20 秒的慢查询是抓不到的——它扫描量可能不足 10 万,出现次数也不够 50 次。要复现,典型做法是建一张 30 万行以上的大表,再用批量任务对它触发 50 次以上的全表扫描类查询。

刚触发完请等下一个采集周期再看;想更容易复现,可调小上表中的两个阈值。

排查:页面一直没有数据

按顺序检查:

  1. profiling 是否已开:在目标库执行 db.getProfilingStatus()was 应为 1slowms 应为 100。仍是 0 说明平台没能打开它,多半是监控账号权限不足。
  2. system.profile 里有没有货
    db.system.profile.find({ docsExamined: { $gte: 100000 } }).sort({ ts: -1 }).limit(5)
    为空说明造的数据没达到扫描量门槛,不是平台的问题。
  3. 上一步有货但页面仍为空:多半是同一指纹不足 50 次,或者还没到 10 分钟的采集周期。

手工启用 profiler(仅当账号权限不足时)

正常情况下平台会自动开启。若日志出现 Permission denied,在每个被监控库手工开启(slowms 为慢查询阈值,生产建议 100~500ms):

use mdwsrows; db.setProfilingLevel(1, { slowms: 100 })
use mdservicedata; db.setProfilingLevel(1, { slowms: 100 })
use mdworksheet; db.setProfilingLevel(1, { slowms: 100 })
use mdworkflow; db.setProfilingLevel(1, { slowms: 100 })

注意事项

  • profiler 有约 1~3% 开销,生产只采 slowms 阈值以上,不要用 level=2(全量采集)。
  • system.profile 是 capped collection(默认 1MB),高 QPS 下滚动很快;需要更长留存可扩容:
    db.setProfilingLevel(0)
    db.system.profile.drop()
    db.createCollection("system.profile", { capped: true, size: 100000000 }) // 100MB
  • 一键建索引前评估对写入的影响,必要时低峰执行。