要,但需分情况:全新集合可删索引再重建以提速3–10倍;已有集合或需实时查询时应保留关键索引,改用background: true创建,避免写冲突和CPU过载。

批量写入前要不要删索引?
要,但得看情况。如果导入的是全新集合、且后续查询不依赖实时索引,停用索引能提速 3–10 倍;但如果集合已有数据、或业务要求写入期间仍需被查询,就别硬删,改用 background: true 创建索引更稳妥。
常见错误是:一边 insertMany 一边后台建索引,结果写入卡在 WriteConflict 或 CPU 拉满——WiredTiger 引擎对写操作和索引构建会争抢 page latch。
- 全新集合导入:先
db.collection.dropIndexes(),插完再db.collection.createIndex({ ... }, { background: false }) - 已有集合追加:保留必要查询索引,禁用非关键字段索引(比如日志时间戳上的冗余索引)
- 不确定时:用
db.collection.stats().indexCount查当前索引数,逐个评估每个索引的accesses.ops(来自db.collection.aggregate([{$indexStats: {}}, {$group: {_id: "$name", ops: {$sum: "$ops"}}}]))
哪些索引会拖慢 insertMany?
不是所有索引都平等。真正拖慢批量写入的,是那些字段更新频繁、值重复率高、或组合键顺序反了的索引。
例如:{ createdAt: 1, status: 1 } 这种索引,在按时间顺序插入时,B+ 树要频繁分裂;而 { status: 1, createdAt: 1 } 就更友好——因为 status 通常离散度低,等价于把数据按 status 分桶后再按时间排。
- 避免在
_id以外的单字段上建唯一索引(除非业务强约束),每次插入都要查唯一性 - 删除
text索引:全文索引构建开销极大,批量写入时几乎必然成为瓶颈 - 警惕数组字段索引:
{ tags: 1 }会让一条文档产生多个索引条目,写放大严重
insertMany 时索引参数怎么配?
驱动层不控制索引行为,但你可以通过写关注和批量策略间接减轻索引压力。
关键不是“关掉索引”,而是让索引更新不成为写入路径的阻塞点。比如设 writeConcern: { w: 1, j: false } 后,主节点写入内存就返回,索引更新可以异步完成;而默认 w: "majority" 会等副本集多数节点刷盘+索引同步,自然慢。
- 批量大小别超
1000条:小批提交让索引更新更平滑,也降低单次 B+ 树调整幅度 - 用
{ ordered: false }:某条因索引冲突失败(如唯一键重复),不影响其余插入,避免整批回滚重试 - 别在事务里跑大 insertMany:事务内索引更新必须原子,WiredTiger 会锁住整个索引结构
导入后重建索引要注意什么?
重建不是简单 run createIndex 就完事。生产环境重建大集合索引,容易触发内存溢出或主从延迟飙升。
真正容易被忽略的是:background: true 虽然不阻塞读写,但它会持续占用 CPU 和 I/O,且无法中断;而 background: false 虽快,但会锁表——哪怕只锁几秒,对高并发服务也是风险。
- 超过千万级文档:优先用
db.collection.createIndex(..., { background: true }),并监控db.currentOp({ "secs_running": { "$gt": 60 } })防止长任务堆积 - 需要最快完成:停写窗口期执行
background: false,但务必提前在从节点上预热索引(用rs.secondaryOk()切到从库建,再切回) - 复合索引顺序验证:用
db.collection.explain("executionStats").find({...}).sort({...})看是否命中,避免重建了个“假索引”


















