mongodb慢查询必须用db.currentop()实时捕获+explain("executionstats")精准归因,而非mongostat或日志grep;因前者仅统计无单条耗时,后者滞后且漏判亚秒级恶化。

直接上结论:MongoDB 慢查询不能靠 mongostat 或定时抓日志“猜”,必须用 db.currentOp() + explain("executionStats") 主动识别真实慢操作,再结合 Python 脚本做阈值判断和告警。
为什么 mongostat 和日志 grep 不适合慢查询监控
mongostat 只显示聚合统计(如每秒 opcount、faults),不暴露单条查询耗时;grep 日志又依赖 slowms 配置且滞后——日志写入本身就有延迟,等你看到“1200ms”那条,业务可能已超时重试三次了。
- 日志方式要求
logLevel: 1+slowOpThresholdMs: 100,但生产环境通常设为 500+,漏掉大量亚秒级恶化查询 -
mongostat的$11(faults)或$12(locked %)是系统级指标,和具体某条find({status: "pending"})执行 800ms 完全无关 - 日志路径、轮转策略、权限问题常导致脚本读不到最新 slow log,误报率高
用 currentOp() 实时捕获运行中慢操作
真正有效的做法是让 Python 脚本直连 MongoDB,调用 db.currentOp() 查当前活跃操作,过滤出执行时间超阈值的会话。这是唯一能发现“正在卡住”的查询的方式。
- 必须加
{ "secs_running": { "$gt": 2 } }这类条件,否则返回全部会话(含 idle conn),数据量爆炸 - 注意权限:用户需有
inprog或clusterMonitor角色,普通 readWrite 用户默认无权调用 - 避免全量扫描:加
limit(20),只取最慢的前 N 条,防止网络阻塞或解析超时 - 示例关键代码片段:
from pymongo import MongoClient<br>client = MongoClient("mongodb://user:pswd@localhost:27017/admin?authSource=admin")<br>db = client.admin<br>slow_ops = list(db.currentOp({ "secs_running": { "$gt": 3 }, "secs_running": { "$exists": True } }).limit(10))<br>for op in slow_ops:<br> print(f"ns: {op.get('ns')}, secs: {op.get('secs_running')}, cmd: {op.get('command', {}).get('find') or 'unknown'}")
对可疑查询补 execStats 精确归因
仅靠 currentOp() 知道“它慢”,但不知道“为什么慢”。此时要提取 op["command"] 中的查询结构,用 explain("executionStats") 复现并分析。
- 别在生产主库上直接跑 explain:先用
readPreference=secondaryPreferred连副本集节点,避免加重主库负担 - 必须用
"executionStats",不是"queryPlanner"—— 后者不返回totalDocsExamined和真实耗时 - 重点盯三个字段:
executionTimeMillis(是否真超限)、totalDocsExamined(是否远大于nReturned)、executionStages.stage(是否出现COLLSCAN) - 如果
currentOp()返回的是聚合管道,得把整个pipeline数组原样传给collection.aggregate(...).explain(),不能只取 $match 部分
Zabbix 或 Prometheus 对接的关键细节
把上述逻辑封装成可被监控系统调用的接口时,最容易翻车的是超时和权限配置。
- Zabbix agent 默认
Timeout=3,而一次currentOp()+ 两次explain()很容易突破 5 秒,必须在zabbix_agentd.conf里显式设Timeout=30 - 脚本里不要硬编码连接串,改用环境变量或配置文件,例如
MONGODB_URI=mongodb://...,方便不同环境切换 - 返回给 Zabbix 的值只能是单个数字(如慢操作数量),不要 JSON 或字符串;若需多维指标(如“慢查询数”“全表扫描数”),得注册多个
UserParameter - Prometheus 场景下,建议用
prometheus_client暴露mongodb_slow_queries_total{ns="mydb.orders"}这类带 label 的指标,比 flat 数值更利于下钻
真正难的不是写脚本,而是区分“偶发慢”和“结构性恶化”:同一查询今天 300ms、明天 2s,大概率是数据分布变化或索引失效;但如果 totalDocsExamined / nReturned 从 1.2 涨到 47,基本可以确定索引崩了——这时候告警内容里必须带上这个比值,而不是只说“查询慢”。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











