mongo内存
🔍 WiredTiger 引擎缓存限制为 8GB,但系统进程 RSS 占用却达到了 12GB。
很多人看到这种情况第一反应是“MongoDB 内存泄漏”,但实际上这往往是 正常的引擎行为 或 可解释的额外内存占用。
下面我分层详细分析这个现象的真正原因、验证方式和判断标准。
🧭 一、问题背景与现象重述
| 指标 | 来源 | 数值 |
|---|---|---|
WiredTiger cache (maximum bytes configured) |
db.serverStatus().wiredTiger.cache |
≈ 8 GB |
| RSS (Resident Set Size) | top / db.serverStatus().mem.resident |
≈ 12 GB |
💡 现象:MongoDB 进程实际驻留内存比引擎配置的 cache 大约多出 4 GB。
🧩 二、关键结论(核心要点)
MongoDB 的 RSS ≠ WiredTiger cache。
RSS 包含:
-
WiredTiger 内部 cache(
storage.wiredTiger.engineConfig.cacheSizeGB) -
索引与表元数据 cache(metadata)
-
压缩与解压缓冲区(zlib/zstd/snappy)
-
Journal 写缓存(预写日志缓冲)
-
Aggregation / $lookup / sort 等临时内存
-
连接缓冲区与 socket buffer
-
线程栈与内核映射页(mmap header / 文件描述符表)
因此,RSS 高于 cache 是预期行为。
只要增幅合理(通常不超过 cache 的 50%),就不是泄漏。
⚙️ 三、分层分析方法(MongoDB 内部命令)
我们仅使用 MongoDB 自身信息(不依赖 Linux 工具)。
1️⃣ 查看 WiredTiger 缓存状态
db.serverStatus().wiredTiger.cache
重点字段:
| 字段 | 含义 |
|---|---|
maximum bytes configured |
8 GB(你的设置) |
bytes currently in the cache |
实际 cache 占用 |
tracked dirty bytes in the cache |
脏页 |
pages evicted by application threads |
驱逐频繁度 |
pages read into cache / bytes read into cache |
读放大情况 |
🔍 如果 bytes currently in the cache 接近上限(例如 7.9GB),说明 WiredTiger 部分是稳定的。RSS 的额外部分来自别处。
2️⃣ 检查数据库与集合占用分布
db.stats()
db.getCollectionNames().forEach(function(c){
var s = db.getCollection(c).stats();
print(c, Math.round(s.wiredTiger.cache["bytes currently in the cache"]/1024/1024), "MB");
});
分析哪些集合在内存中最活跃。
如果发现个别集合 cache 占用非常高,说明热点数据集中在这部分。
3️⃣ 检查连接数与游标
db.serverStatus().connections
db.serverStatus().metrics.cursor
| 指标 | 含义 | 说明 |
|---|---|---|
connections.current |
当前连接数 | 每个连接约 1 MB 内存 |
metrics.cursor.open.total |
活跃游标 | 长时间未关闭的 cursor 会占缓存 |
如果连接数高(例如 3000),就会额外吃掉 3 GB 内存。
4️⃣ 检查聚合/排序类操作的内存使用
db.currentOp({ "active": true })
关注:
-
"op": "command" -
"command.aggregate"存在 -
"usedDisk": false
如果有 $group、$sort、$lookup 且 usedDisk=false,说明这些操作在内存中堆积中间结果,会造成瞬时内存暴涨。
这些不会计入 WiredTiger cache。
5️⃣ 检查 journal / 压缩缓冲区
虽然 Mongo 没有直接字段展示 journal buffer,但可以通过:
db.serverStatus().wiredTiger.transaction
查看:
-
"transaction checkpoints"频率(如果很低) -
"transaction range of IDs currently pinned"较大
→ 表示 checkpoint 延迟、脏页堆积
压缩缓冲区和 journaling 通常会占用 0.5–1 GB 额外内存。
🧮 四、WiredTiger 内部结构导致的额外内存来源
| 模块 | 典型占用 | 说明 |
|---|---|---|
| WT cache | 8 GB | 配置上限 |
| 索引元数据(btree metadata) | ~0.5–1 GB | schema + handle cache |
| 压缩缓冲区(Snappy / Zstd) | 0.5–2 GB | 压缩块缓存与线程缓冲 |
| Journal buffer | 0.5–1 GB | 写日志缓存 |
| Aggregation / Sort / Cursor 内存 | 动态 | pipeline 或 client 端操作 |
| 连接与线程栈 | 0.5–1 GB | 每连接 + 线程栈 |
| 内核 mmap header 与共享页 | 约数百 MB | 文件描述符与 mmap 区头部 |
合计:8 GB + ~4 GB = 12 GB
完全匹配你的现象。
🔍 五、判断是否异常(是否属于“内存泄漏”)
| 判断点 | 正常情况 | 异常提示 |
|---|---|---|
bytes currently in the cache ≈ 设定上限 |
✅ 正常 | ❌ 持续超过上限(不可能) |
| RSS / cache 比例 < 1.5 | ✅ 正常范围 | ❌ >2 倍说明异常增长 |
pages evicted by application threads 稳定 |
✅ 正常 | ❌ 持续爆增 |
connections.current 稳定 |
✅ 正常 | ❌ 持续上升 |
db.currentOp() 长时间高内存聚合 |
❌ 出现时临时暴涨 | |
serverStatus().mem.resident 稳定 |
✅ 正常 | ❌ 持续爬升说明泄漏或长生命周期对象堆积 |
🧰 六、可执行诊断步骤(仅用 MongoDB 命令)
你可以按顺序执行下列命令:
// 1. 全局内存摘要
db.serverStatus().mem
// 2. WiredTiger cache 占用
db.serverStatus().wiredTiger.cache
// 3. 数据库与集合 cache 分布
db.stats()
db.getCollectionNames().forEach(function(c){
var s = db.getCollection(c).stats();
print(c, Math.round(s.wiredTiger.cache["bytes currently in the cache"]/1024/1024), "MB");
});
// 4. 当前连接与游标
db.serverStatus().connections
db.serverStatus().metrics.cursor
// 5. 检查是否存在占用内存的聚合
db.currentOp({ "active": true, "op": "command" })
执行完你就能判断:
-
是否热点数据或聚合导致;
-
是否连接/游标堆积;
-
WiredTiger cache 是否真的满;
-
额外 4 GB 是否可解释。
🧩 七、若确实异常(超出正常范围)的应对
| 可能原因 | 验证方式 | 优化措施 |
|---|---|---|
| 聚合或排序大量内存 | db.currentOp() |
启用 allowDiskUse:true |
| 连接数暴涨 | db.serverStatus().connections |
启用连接池 / 限制连接 |
| 缓存配置过小,导致 page 频繁驱逐 | pages evicted 高 |
提高 cacheSizeGB |
| checkpoint 延迟导致脏页堆积 | transaction 状态 |
调整 checkpoint=(wait=60) |
| 压缩缓冲区膨胀 | 无直接指标 | 换算法(Snappy→Zstd)或更新 Mongo 版本 |
| 游标未关闭 | metrics.cursor.open.total 高 |
确保客户端关闭 cursor |
✅ 八、总结(面试/生产解答版)
| 结论 | 说明 |
|---|---|
| RSS > WiredTiger cache 是正常现象 | 因为 RSS 包含其他内存区 |
| 一般情况下 1.3~1.6 倍属正常 | 例如 cache 8G → RSS 12G |
| 可通过 serverStatus() 内部分析验证 | 不需系统工具 |
| 异常增长常来自聚合、连接、压缩缓冲或 journaling | 可用 currentOp 和 connections 验证 |
| 若 RSS 超 cache 2 倍以上且长期不回落 | 可能存在内存泄漏或长生命周期对象问题 |
浙公网安备 33010602011771号