必须先在 MongoDB 服务端执行 db.setProfilingLevel(),否则 Node.js 层无法获取慢查询数据;profile: 1 记录超阈值操作,profile: 2 记录全部(禁用于生产);system.profile 不支持 changeStream,须轮询并建 ts 索引;客户端钩子可补充监控,但与服务端 profiling 机制独立,需两者结合才能准确定位瓶颈。

必须先在 MongoDB 服务端执行 db.setProfilingLevel(),否则 Node.js 层永远收不到慢查询数据。 驱动本身不生成日志,只读取已记录的内容;没开 profiling,system.profile 集合压根不会创建,所有监听和轮询都查不到东西。
db.setProfilingLevel 是硬性前置条件
常见错误是只改 Node.js 代码(比如加 slowquery 监听或轮询 system.profile),却忘了进 MongoDB shell 执行配置:
- 连接目标库后运行:
use your_db_name,再执行db.setProfilingLevel(1, { slowms: 200 }) -
slowms是毫秒阈值,设为200表示记录所有耗时 >200ms 的操作 - 生产环境建议从
500起步,避免日志写爆磁盘或拖慢主库 -
profile: 1只记录慢操作;profile: 2记录全部(仅调试用,严禁上生产)
不能对 system.profile 使用 changeStream
system.profile 是 capped collection,changeStream 明确不支持——直接监听会报 NamespaceNotFound 或 CommandNotSupported。
- 替代方案只有轮询:
db.system.profile.find({ ts: { $gt: lastSeenTs } }).sort({ ts: 1 }) - 必须手动建索引:
db.system.profile.createIndex({ ts: 1 }),否则每次全表扫描极慢 - 每次查完要更新
lastSeenTs(ISODate 类型),避免重复读取 - 轮询间隔 ≥1s,太密会让轮询查询本身变成慢操作
用 driver 的 commandStarted/commandSucceeded 做轻量拦截
Driver 4.0+ 提供客户端命令生命周期钩子,适合做实时耗时统计,不依赖服务端 profiling:
- 事件是 client 级别,不是 db 或 collection 实例上的方法
- 若用 Mongoose,需从
mongoose.connection.client获取底层 client 实例 - 能捕获发到 Driver 的请求耗时,但无法覆盖网络超时、连接中断等服务端未收到的场景
- 示例关键逻辑中,
event.durationStart = process.hrtime()必须在commandStarted里赋值,且commandSucceeded中要用同一 event 对象计算
真正容易被忽略的是:服务端 profiling 和客户端 hook 是两套独立机制,前者记录真实服务端执行情况,后者只反映客户端视角。如果只依赖其中一种,会漏掉关键瓶颈——比如慢查询卡在锁等待、索引失效或网络抖动上,它们可能一个显示“快”,另一个显示“慢”。











