慢查询会导致连接池满载,因为长执行查询持续占用连接不释放;node.js驱动默认poolsize=10,一旦并发中多个查询超时或卡住(如collscan、缺失索引),连接无法归还,后续请求在waitqueue排队,最终触发mongowaitqueuefullexception或超时。

连接池满载,八成不是池子太小,而是查询卡在某处没回来——慢查询会把连接占着不放,直到超时或手动释放。
为什么慢查询会导致连接池满载
Node.js 的 mongodb 驱动(v4+)默认 poolSize 是 10,但这个“池”是每个 MongoClient 实例独享的。一旦某个查询执行时间过长(比如全表扫描、缺失索引、跨分片聚合),它就会持续占用一个连接,无法归还给池子。并发稍高时,10 个连接很快被耗尽,后续请求在 waitQueue 中排队,最终触发 MongoWaitQueueFullException 或静默超时。
常见现象包括:
- 日志里反复出现
MongoWaitQueueFullException或Timed out after 5000ms while waiting for a server -
db.currentOp()显示大量active状态且secs_running> 1000 的操作 - 应用 CPU 不高,但 MongoDB
db.serverStatus().connections.current持续接近available上限
用 profiling 快速确认是否真有慢查询在堵路
别猜,直接看 system.profile。先临时开启(生产环境建议阈值设为 100ms 起):
db.setProfilingLevel(1, { slowms: 100 })
等 2–5 分钟后查最近的慢操作:
db.system.profile.find({ millis: { $gt: 100 } }).sort({ ts: -1 }).limit(5)
重点关注字段:
ZenHeart zenlink 与 zenlink-mcp 为 OpenClaw,涵盖安装、MCP 注册、钩子接线、入站轮询及升级维护。
-
ns:哪个库/集合被查了 -
planSummary:如果是COLLSCAN,基本可断定缺索引 -
docsExamined远大于nReturned:说明做了大量无用扫描 -
locks里acquireCount.w高但secs_running长:可能是写锁阻塞或事务未提交
explain() 定位具体哪条语句拖垮了连接
从 profiling 找到可疑查询后,在 shell 里复现并加 explain("executionStats"):
db.users.find({ status: "inactive", created_at: { $lt: ISODate("2025-01-01") } }).explain("executionStats")
关键看三组数字:
-
executionTimeMillis:总耗时(>100ms 就得管) -
totalKeysExamined:索引扫描行数(越小越好) -
totalDocsExamined:文档扫描行数(如果和totalKeysExamined差 10 倍以上,大概率没走对索引)
如果 executionStats 里 stage 是 IXSCAN 但 totalKeysExamined 仍很大,说明索引字段顺序或选择性不够,得调索引;如果是 COLLSCAN,立刻补复合索引,别犹豫。
连接池参数与慢查询必须协同调优
只调 poolSize 是掩耳盗铃。Node.js 驱动里真正影响连接复用的是这三项:
-
maxPoolSize:设为 10–20 即可,别盲目拉到 100(多数业务 15 足够) -
minPoolSize:设为 5,避免冷启动时频繁建连 -
maxIdleTimeMS:设为 60000(60 秒),让空闲连接及时回收,防止长连接堆积
同时必须配合慢查询治理:
- 所有带
$lookup、$facet、跨分片的聚合操作,必须加allowDiskUse: true并确保内存充足,否则会卡死连接 - 写操作务必加
writeConcern: { w: "majority" },避免因副本集同步延迟导致连接挂起 - 禁止在循环里反复
client.db().collection()—— collection 实例应复用,否则每次新建都可能触发新连接申请
最易被忽略的一点:explain() 本身也会走连接池。如果 profiling 已发现慢查询,别在高峰期反复跑 explain,先用 db.currentOp({ secs_running: { $gt: 5 } }) 快速抓活体,再针对性分析。










