MongoDB写入时内存暴涨是WT缓存、连接线程栈、聚合排序缓冲区、日志缓冲区、tcmalloc碎片及压缩解压等多模块共同作用的结果,非仅cacheSizeGB可控制。

MongoDB写入时内存暴涨,不是“缓存没配好”就能一语带过的——它背后往往是多个内存模块在同时吃资源,而WiredTiger只是最显眼的那个。
cacheSizeGB 不能拦住所有内存增长
cacheSizeGB 只限制 WiredTiger 存储引擎的内部缓存(即 page cache),但 MongoDB 进程整体内存 = WT cache + 连接线程栈 + 聚合/排序临时缓冲区 + 日志缓冲区 + tcmalloc 内存碎片 + 压缩解压中间态。
- 默认情况下,cacheSizeGB 未显式设置时,WiredTiger 会取 (系统内存 - 1GB) / 2 和 256MB 的较大值 —— 在 64GB 机器上就是约 31.5GB,远超实际工作集;
- 即使设了 cacheSizeGB: 8,WT 仍可能短暂突破该值(尤其在批量写+高并发时),因为脏页淘汰(eviction)有延迟;
- 更关键的是:cacheSizeGB 对连接内存、聚合内存、日志缓冲区完全无效。
写入密集场景下,线程栈和连接数是隐形杀手
每个客户端连接对应一个线程,每个线程默认占用约10MB 栈空间(Linux ulimit -s 默认值)。
- 若应用使用 500 个长连接池,仅线程栈就吃掉 5GB;
- 如果 mongos → mongod 连接未复用(releaseConnectionsAfterResponse 关闭),连接数可能指数级堆积;
- 错误现象:mongostat 显示 conn 持续上涨,top 中 RES 高但 cacheSizeGB 并未打满;
- 解法:
- 启用 releaseConnectionsAfterResponse: true(mongos 配置);
- 降低 OS 线程栈大小:ulimit -s 1024(改到 1MB);
- 服务端强制限制最大连接:maxConns: 2000(写入型负载建议 ≤1000)。
批量写 + 无索引维护 + 大文档 = 内存雪崩组合拳
高频批量插入(如每秒千级)若叠加以下条件,极易触发 OOM: - 插入前未禁用索引(db.collection.createIndex(..., {background: true}) 不等于“不占内存”,建索引过程本身大量消耗 RAM);
- 文档平均体积大(如单条 >100KB),WT 在内存中保留多版本(MVCC)+ 压缩前原始数据 + 日志缓冲区;
- 使用 aggregate 或 $lookup 辅助写入,触发 allowDiskUse: false 下的内存排序;
- 错误日志典型特征:Cannot allocate memory 出现在 eviction-server 上下文,紧接着 WT_PANIC。
- 应对动作:
- 批量写前执行 db.runCommand({setParameter: 1, wiredTigerEngineRuntimeConfig: "eviction=(threads_max=8,threads_min=4)"})(仅 4.0+ 副本集);
- 插入阶段临时删除非必要索引,写完再重建;
- 控制单批大小(bulkWrite 推荐 100–500 条/批,避免单次分配超 100MB 临时 buffer)。
别只盯着 db.serverStatus().wiredTiger.cache
真正定位内存去向,要交叉看三组指标: -db.serverStatus().mem:看 resident(真实 RSS)是否远高于 cacheSizeGB;
- db.serverStatus().tcmalloc:若 malloc: bytes in use by application 异常高,说明应用层(如聚合、JS 引擎)或内存泄漏;
- db.serverStatus().connections:比对 current 与 available,差值过大说明连接堆积;
- 最容易被忽略的一点:Docker 容器未设 --memory 限制时,OOM Killer 会直接杀 mongod,此时 cacheSizeGB 设置再准也白搭。
内存暴涨从来不是单一配置问题,而是 WT 缓存、连接模型、写入模式、容器边界四者共同作用的结果。调参前,先确认你压的是哪一层。


















