MongoDB建索引时OOM主因是排序阶段独占堆内存(sortBufferSizeMB默认512MB),不受WiredTiger缓存限制;应调小该参数(64–2048)、禁用并发建索引、优先用partial index分批构建,并监控RES与createIndex耗时。

建索引时内存暴涨,mongod 被 OOM killer 干掉怎么办
直接结论:MongoDB 在后台建索引(尤其是 background: true)仍会大量使用内存,排序阶段默认用 sortBufferSizeMB(512MB)+ 内存映射文件,一旦数据量大或并发建索引,很容易触发系统 OOM。这不是配置没开、也不是 bug,是设计使然。
关键点在于:索引构建分两阶段——扫描文档 + 排序键值。第二阶段的排序缓冲(sort buffer)不走 WiredTiger cache,而是直用堆内存,且无法被 storage.wiredTiger.engineConfig.cacheSizeGB 限制。
- Linux 下用
dmesg -T | grep -i "killed process"确认是否真被 OOM killer 杀过; - 查
mongod日志里有没有Sort key buffer size exceeded或反复出现Creating index后卡住; - 建索引前先
db.collection.stats()看文档数和平均大小,预估键值总量(比如 10M 文档 × 200B 键 ≈ 2GB 排序数据); - 别在高峰期跑
createIndex({a: 1, b: 1})这种复合索引,尤其字段含字符串或嵌套对象——键更大、排序更耗内存。
sortBufferSizeMB 参数到底能不能关小
能,但有硬下限和副作用。从 MongoDB 4.2 开始,sortBufferSizeMB 可设为 64–2048,默认 512。低于 64 会被自动拉回 64;设太高(比如 2GB)反而可能让单次排序失败率上升(因内存分配失败或触发系统压力)。
它只影响单个索引构建过程中的排序阶段,不影响查询缓存或 WiredTiger cache。改法是在启动时加:--setParameter sortBufferSizeMB=128,或运行时:db.adminCommand({setParameter: 1, sortBufferSizeMB: 128})(需 clusterAdmin 权限)。
- 该参数不能热重载所有场景:如果已有建索引任务在跑,新值要等下次建索引才生效;
- 设太小(如 64MB)会导致频繁落盘排序(spill to disk),建索引时间翻倍甚至更多,磁盘 I/O 暴涨;
- 在容器环境(如 Kubernetes)中,还要同步调低容器内存 limit,否则
sortBufferSizeMB设再小,mongod进程仍可能因 RSS 超限被 kill。
不用 background: true 就安全了?
不安全。所谓 background: true 只是避免锁表(用 yield 方式间歇让出读写锁),并不降低内存用量。事实上,后台模式因执行周期拉长、中间状态保留更久,有时内存驻留时间反而更长。
真正省内存的做法是:用 foreground 模式 + 控制数据范围。比如对超大集合,先按时间/状态分批建索引:
db.collection.createIndex({ts: 1}, {background: false})
// 然后用 partialFilterExpression 缩小范围
db.collection.createIndex({status: 1, user_id: 1}, {
background: false,
partialFilterExpression: {status: {$in: ["active", "pending"]}}
})
- Partial index 不仅减少索引体积,也大幅压缩排序阶段要处理的键数量;
- Foreground 建索引虽阻塞写入,但生命周期短、内存释放快,适合维护窗口期明确的集群;
- 千万别在同一个集合上并发跑多个
createIndex—— 排序缓冲是进程级的,不是会话级,多个任务会抢同一块内存池。
监控和应急:怎么提前发现、临时止损
靠 top 或 htop 看 RES(常驻内存)只是马后炮。真正有用的指标是 MongoDB 自身暴露的:serverStatus().mem 里的 bits(当前内存位图)、wiredTiger.cache 的 bytes currently in the cache,以及 Linux 的 /sys/fs/cgroup/memory/memory.usage_in_bytes(如果用了 cgroup)。
- 加告警:当
process.memory.resident> 80% 容器 limit,且serverStatus().metrics.operation中createIndex持续 > 30 分钟,就该人工介入; - 紧急停索引:没有官方“取消建索引”命令,只能
kill -2 <pid></pid>(SIGINT)优雅终止,mongod 会清理临时文件,但已写入的索引页不会回滚; - 事后检查:删掉失败索引后,用
db.collection.getIndexes()确认无残留"inProgress": true状态,否则下次重启可能重试并再次 OOM。
最易被忽略的一点:WiredTiger cache 大小设置和 sortBufferSizeMB 是两套内存管理体系,调大 cache 不会缓解排序 OOM,调小 sort buffer 也不影响查询性能——它们管的是完全不同的事,混着调只会让问题更难定位。

















