一眼识别collscan导致性能问题:cpu持续高于70%且无业务峰值时,explain("executionstats")中stage为"collscan"、docsexamined远大于nreturned(如扫50万返10条),或慢日志/system.profile中出现collscan记录。

怎么一眼看出是 COLLSCAN 导致的性能问题
只要 MongoDB CPU 持续高于 70% 且无明显业务峰值,就该立刻怀疑 COLLSCAN —— 它不是偶发现象,而是持续扫描整个集合的“CPU 吸尘器”。关键信号是:explain("executionStats") 返回里 "stage": "COLLSCAN" 出现,同时 docsExamined 远大于 nReturned(比如查 10 条返回,却扫了 50 万文档)。别信 indexName 字段为空就一定是没索引——有些复合索引会“部分命中”,但因字段顺序错或类型不匹配,实际仍走 COLLSCAN。
用 db.currentOp() 抓正在跑的 COLLSCAN
直接连上 MongoDB Shell,执行这个命令,能实时揪出正在全表扫描的活体查询:
db.getSiblingDB("admin").aggregate([
{ $currentOp: { allUsers: true } },
{ $match: { "planSummary": "COLLSCAN", secs_running: { $gt: 2 } } },
{ $project: { ns: 1, query: 1, secs_running: 1, client: 1 } }
])
注意三点:
-
planSummary: "COLLSCAN"比type: "query"更准,过滤掉无关操作 -
secs_running > 2是起点阈值,线上可先设为 3–5 秒,避免噪音 - 返回里的
ns(命名空间)告诉你具体是哪个库哪个集合在被扫,query字段就是原始查询条件,直接拿去下一步 explain
explain("executionStats") 必须带参数运行
对上一步拿到的查询,**不能**只跑 find(...).explain() —— 默认模式只返回查询计划概要,看不到真实扫描量。必须显式传参:
db.orders.find({ status: "pending", created_at: { $gt: ISODate("2026-07-01") } }).explain("executionStats")
重点盯这几个字段:
-
executionStats.executionStages.stage:确认是不是COLLSCAN,不是IXSCAN或PROJECTION -
executionStats.docsExamined:实际读了多少文档,超过集合总文档数 10% 就危险 -
executionStats.nReturned:真正返回多少,如果比 docsExamined 小两个数量级,说明筛选效率极低 -
executionStats.executionTimeMillis:整条查询耗时,但别单独看它——慢是结果,COLLSCAN 才是根因
慢日志里搜 COLLSCAN 比人工盯 currentOp 更稳
生产环境建议开启 profiling,比临时抓 currentOp 更可靠:
db.setProfilingLevel(1, { slowms: 20 })
然后查最近的 COLLSCAN 记录:
db.system.profile.find({ "millis": { $gt: 20 }, "planSummary": "COLLSCAN" }).sort({ "ts": -1 }).limit(5)
好处是:
- 不依赖“当前正在跑”,能回溯过去几小时的慢查询
-
system.profile里有完整command字段,包含真实执行的 find/update/aggregate 命令,复制粘贴就能复现 - 字段
keysExamined和docsExamined都有,能判断是纯没索引(两者都大),还是索引失效(keysExamined小但docsExamined大)
最常被忽略的一点:COLLSCAN 不一定代表没建索引,而可能因为查询字段顺序和索引字段顺序不一致、用了 $regex 非前缀匹配、或数组字段没建对 multikey 路径——这些情况 explain 里 stage 仍是 COLLSCAN,但原因各不相同,得挨个排除。











