MongoDB需显式启用profiling并设slowms阈值才能记录慢查询;执行db.setProfilingLevel(1, {slowms: 100})开启仅记录>100ms操作,各库需单独设置,通过db.getProfilingStatus()确认生效。

MongoDB 默认不记录单条 CRUD 操作的耗时,必须显式开启 profiling 级别并指定阈值,否则你永远看不到哪条 find() 卡了 800ms。
如何启用 profiling 并设置慢查询阈值
profiling 是数据库级开关,影响整个数据库的写日志行为。它有三个级别:0(关闭)、1(记录慢操作)、2(记录所有操作)。生产环境严禁用 2,IO 和磁盘压力会陡增。
实操建议:
- 先连到目标数据库(如
myapp),执行db.setProfilingLevel(1, { slowms: 100 }):只记录 >100ms 的操作 -
slowms是毫秒单位,不是百分比或相对值;设为0等价于level=2,等同自毁 - 该设置**不继承**,每个数据库需单独执行;
admin库的 profiling 不影响user库 - 确认生效:运行
db.getProfilingStatus(),返回{ "was": 1, "slowms": 100 }才算成功
从 system.profile 里查哪条 CRUD 慢了
system.profile 是一个 capped collection,自动轮转,只保留最近 N 条(默认上限 1MB)。它不支持索引,所以查得越久越卡——别直接 db.system.profile.find() 全扫。
关键过滤条件必须加:
- 按时间范围缩小:用
ts字段,例如{ ts: { $gt: new Date(Date.now() - 5 * 60 * 1000) } }查最近 5 分钟 - 按操作类型聚焦:
{ op: "query" }或{ op: "update" },避免混入command类杂项 - 按耗时排序:加上
.sort({ millis: -1 }),一眼看到最慢那几条 - 只取关键字段:
.project({ ns: 1, op: 1, millis: 1, query: 1, planSummary: 1 }),避免传输大 document 拖慢 shell
典型命令:db.system.profile.find({ op: "query", millis: { $gt: 500 } }).sort({ millis: -1 }).limit(10)
planSummary 字段为什么比 millis 更值得盯
millis 告诉你“有多慢”,planSummary 告诉你“为什么慢”。同一个 find({ status: "pending" }),可能一次走索引(IXSCAN),一次全表扫描(COLLSCAN)——耗时差 100 倍,但 millis 都是 420。
常见 planSummary 值含义:
-
IXSCAN:走了索引,健康信号;但若后面跟着FETCH+ 大量DOCS,说明索引选得不好或投影太宽 -
COLLSCAN:没走索引,第一反应是加索引,但也可能是查询用了$regex开头、或字段类型不匹配 -
SORT:内存排序溢出到磁盘(spills字段非零),需要加复合索引覆盖 sort+query -
TEXT:全文检索,注意score是否合理,避免低相关性结果被拉进来
profiling 对性能的真实影响有多大
开 level=1 后,每条超阈值的操作要额外写一条 document 到 system.profile,本质是一次小写入。影响取决于两个变量:
- 慢操作频率:如果每秒只有 1–2 条慢查询,基本无感;如果每秒几十条,
system.profile的写放大和锁竞争会反向拖慢主业务 - 部署拓扑:副本集里 profiling 只在 PRIMARY 生效,SECONDARY 不记录;分片集群中,每个 shard 独立 profiling,
mongos不参与 - 替代方案更轻量:对高频接口,优先用 driver 层的命令监控(如 Node.js 的
commandStarted事件),或用explain("executionStats")抽样分析热点查询
真正容易被忽略的是:profiling 日志里的 ts 是操作**结束时间**,不是开始时间;如果你在查“某次请求为什么超时”,得结合应用层打点时间戳交叉比对,否则会误判网络延迟为数据库耗时。











