Mongos内存持续上涨主因是连接数过多、游标未及时清理及连接池配置不当;其不使用WiredTiger缓存,内存消耗集中于线程栈(每连接最多1MB)和游标元信息,需通过控制连接数、缩短游标生命周期、合理设置minPoolSize/maxPoolSize及maxIdleTimeMS来优化。

Mongos 本身不存储数据、不维护 WiredTiger 缓存,它的内存占用主要来自连接管理、查询路由缓存和游标维持 —— 所以“降低 Mongos 内存”本质是控制连接数、缩短游标生命周期、避免长连接堆积。
为什么 Mongos 内存会持续上涨?
不是因为缓存数据,而是它为每个客户端连接分配线程栈(默认最多 1MB),同时维护大量活跃游标(cursor)的元信息。当应用端不及时消费或游标超时未清理,Mongos 就会持续持有这些资源。
- 常见错误现象:
mongostat显示conn数长期高于 200,net的in/out波动小但mem持续上升 - 真实瓶颈往往不在 Mongos 进程本身,而在后端
mongod节点的游标压力传导过来 —— Mongos 只是“中转站”,但它会放大上游连接行为 - 特别注意:Mongos 不读取
wiredTigerCacheSizeGB配置,调这个参数对 Mongos 完全无效
连接池配置必须设 minPoolSize 和 maxPoolSize
很多驱动默认只设 maxPoolSize=100,但 minPoolSize=0,导致低峰期连接被全部释放,高峰又得重建 —— 这反而加剧 Mongos 线程创建/销毁开销和瞬时内存毛刺。
- 推荐组合(以 Node.js +
mongodb驱动为例):minPoolSize: 10,maxPoolSize: 50,maxIdleTimeMS: 60000 -
maxIdleTimeMS设太长(比如 30 分钟)会让空闲连接滞留,占着 Mongos 的线程槽位;设太短( - Java 驱动要注意:
connectTimeoutMS默认可能是 10000(版本 3.7+),但 Mongos 前置了负载均衡器时,建议显式设为5000,避免连接卡在 LB 层不反馈 - 别信“连接越多越快”—— Mongos 的路由逻辑是单线程分发的,连接数超过后端
mongod节点数 × 2 后,吞吐基本不再提升,纯增内存负担
游标超时必须由客户端主动控制,不能依赖服务端
Mongos 会把游标请求透传给 mongod,但自身不管理游标超时逻辑。一旦客户端用 find() 拿到游标却不消费完,或消费太慢,mongod 端游标过期后 Mongos 仍可能缓存部分状态,造成内存残留。
- Python / PyMongo 场景:必须加
.batch_size(50)或.no_cursor_timeout(False)(显式关掉),禁用默认的 10 分钟游标保活 - Node.js 场景:用
cursor.toArray()替代for await...of处理大数据集,避免游标长时间挂起;若必须流式处理,务必加timeoutMS选项 - 关键区别:
socketTimeoutMS控制单次读操作,maxTimeMS控制整个查询生命周期,而游标存活靠的是底层心跳 —— 所以真正有效的是在应用层做分页或限制limit() - 线上已见案例:某服务用
find().sort({ts:-1}).limit(1000)却没加maxTimeMS,结果因索引缺失导致扫描全表,游标卡住 12 分钟,Mongos 对应连接内存涨到 80MB+
真正该监控的不是 Mongos RSS,而是 connections 和 active cursors
用 db.currentOp({ "secs_running": { "$gt": 60 } }) 在 Mongos 上执行,看到的只是它转发的命令;真要定位问题,得去每个分片的 mongod 上查 db.currentOp({ "secs_running": { "$gt": 30 }, "secs_running": { "$exists": true } })。
- 一个常被忽略的事实:Mongos 的
top输出里active字段统计的是它自己正在处理的请求,不包括已转发出去、还在mongod执行的游标 —— 所以看 Mongos 进程内存高,十有八九是后端节点扛不住了 - 最有效的降内存动作其实是:在业务低峰期执行
db.runCommand({ "killCursors": "collection_name", "cursors": [cursor_id] })清理遗留游标(需先从db.currentOp()中提取) - 如果 Mongos 内存持续 > 1.5GB 且连接数稳定在 100 以下,优先检查是否启用了
sh.setBalancerState(false)后忘记关,导致迁移任务堆积在 Mongos 队列里











