MongoDB在运行过程中如果出现故障码310,大概率意味着实例因为内存压力过大而进入异常状态,轻则拒绝写入,重则进程被操作系统强制终止。内存溢出问题在MongoDB的运维场景里相当常见,尤其是部署在小内存服务器或者与其他服务混部的环境中。要彻底解决这个问题,不能只靠重启实例,必须搞清楚内存到底被谁占用了、WiredTiger缓存的工作机制是怎样的,以及操作系统的OOM Killer在其中扮演了什么角色。

一、故障码310的成因:先搞清楚MongoDB的内存结构
很多运维人员一看到内存溢出,第一反应就是加内存。但MongoDB的内存使用远比想象中复杂,它主要由几部分构成:WiredTiger存储引擎的内部缓存、文件系统缓存、连接和游标占用的内存、聚合排序时的临时内存,以及索引构建产生的额外开销。故障码310触发的直接原因,通常是这些内存的总和逼近甚至超过了操作系统的物理内存上限,导致关键操作无法分配到足够的内存。
WiredTiger缓存是内存消耗的大头。默认情况下,MongoDB会根据机器总内存来计算缓存大小:内存大于等于2GB时,默认缓存约为(总内存 - 1GB)的50%。问题在于,这个默认值是在MongoDB独占整台机器的前提下设计的。如果你的服务器上还跑着应用服务、Redis或者其他MongoDB实例,多个进程各自按照50%的规则去申请内存,总消耗自然会超过物理内存,最终触发溢出。
另一个容易被忽视的因素是文件系统缓存。MongoDB读取数据文件时依赖操作系统层面的页缓存,这部分内存不受MongoDB进程控制,却实实在计算在系统总占用里。此外,当执行大结果的排序或聚合时,如果allowDiskUse没有开启,中间结果全部堆在内存里,也是内存暴涨的常见诱因。
二、排查步骤:从日志和系统指标入手定位问题
遇到故障码310后,第一步不要急着重启,先把日志和系统状态收集齐全。MongoDB的日志文件中通常会记录WiredTiger缓存的使用情况,重点搜索以下关键字:
# 在MongoDB日志中查找内存相关记录 grep -E "wtcache|OutOfMemory|310" /var/log/mongodb/mongod.log | tail -50 # 查看是否有OOM Killer介入的痕迹 dmesg | grep -i "out of memory" dmesg | grep -i "killed process"
如果dmesg输出中出现了Killed process mongod的字样,说明是Linux的OOM Killer把MongoDB进程杀掉了,这属于系统级的强制回收,MongoDB自身无法拦截。此时要重点检查系统内存的真实占用分布,可以用free -h和top命令观察。如果mongod进程的RSS内存持续接近甚至超过你设定的WiredTiger缓存上限,说明除了缓存之外还有其他组件在大量吃内存,比如过多的连接或未关闭的游标。
除了系统层面,MongoDB内部也有现成的诊断命令。db.serverStatus()返回的wiredTiger.cache字段包含了缓存命中率、当前占用的字节数、未在缓存中的读请求数等关键指标。如果tracked dirty bytes持续处于高位,说明写入压力集中在缓存里刷不出去;如果pages requested from the cache暴涨,则意味着工作集大小超过了缓存容量,频繁发生换页。
// 查看WiredTiger缓存的详细状态 db.serverStatus().wiredTiger.cache // 查看当前连接数是否异常 db.serverStatus().connections // 查看是否有大量未关闭的游标 db.serverStatus().metrics.cursor
三、解决方案:从配置参数到架构层面的完整修复
最直接有效的手段是显式限制WiredTiger缓存大小。在mongod的配置文件中设置storage.wiredTiger.engineConfig.cacheSizeGB,把它压到一个安全的数值。经验法则是:缓存大小不要超过(可用物理内存 - 系统与其他进程预留内存)的60%。例如一台16GB的机器上还跑着应用服务,预留4GB给系统和其他进程,那么cacheSizeGB设置为7到8是比较稳妥的。
# /etc/mongod.conf 配置示例
storage:
dbPath: /data/mongodb
wiredTiger:
engineConfig:
cacheSizeGB: 8
journalCompressor: snappy
systemLog:
destination: file
path: /var/log/mongodb/mongod.log
logAppend: true
processManagement:
fork: true
net:
port: 27017
maxIncomingConnections: 500
调整完缓存后,还需要处理连接层面的内存泄漏。每一个客户端连接大约占用1MB左右的栈空间,连接数上千时光连接本身就要吃掉1GB以上内存。可以通过net.maxIncomingConnections限制最大连接数,并在应用侧强制使用连接池复用连接。对于慢查询导致的游标堆积,建议设置合理的超时参数,并排查应用代码中是否存在未调用cursor.close()的情况。
第三个方向是优化查询本身。凡是涉及大范围排序、未加索引的全表扫描、无限制的聚合管道,都会让内存瞬间飙升。开启allowDiskUse可以让聚合中间结果落盘,牺牲一部分速度换取内存安全;给排序和过滤字段建立合适的索引,则能从根源上减少MongoDB需要加载进内存的数据量。如果是分页场景,尽量用基于游标的分页替代大skip偏移量查询,因为skip越深,服务器需要在内存中保留的中间数据越多。
最后,从架构角度出发,如果业务数据量确实已经增长到单机内存扛不住的程度,就该考虑水平拆分了。通过分片把工作集分散到多个节点上,每个分片只需要在缓存中维护自己那部分热数据,这是应对数据规模持续增长的根本办法。同时建议开启监控告警,对mongod进程RSS内存、缓存使用率、系统可用内存设置阈值,在问题恶化到触发故障码310之前就能收到预警,把被动救火变成主动防护。
四、修复后的验证与预防措施
配置修改完成后,重启mongod进程并持续观察一段时间。验证时重点关注三个指标:一是mongod进程的RSS内存是否稳定在预期范围内,不再持续爬升;二是WiredTiger缓存命中率是否保持在95%以上;三是系统层面是否还有swap交换发生。可以写一个简单的定时脚本,把这几个指标记录下来形成趋势,方便后续复盘。
预防层面还有两个细节值得注意。其一,swap的设置要谨慎,MongoDB官方建议要么完全关闭swap,要么把vm.swappiness调到1左右,因为数据库进程被换出到swap会显著拉低性能,甚至造成长时间卡顿引发副本集心跳超时和主从切换。其二,副本集环境下所有节点都要统一调整cacheSizeGB,避免只改了主节点而secondary节点内存照样爆掉的情况。备份节点、延迟节点同样承载完整数据集,它们的内存需求与主节点是一样的,这一点在规划机器规格时经常被漏算。
MongoDB故障码310MongoDB内存溢出MongoDB内存优化修改时间:2026-09-14 08:36:39