reindex 会阻塞副本集全部写入并导致同步延迟;应改用 dropindex + createindex 滚动重建,需在主节点执行、确保 majority 写关注、检查索引一致性并验证新索引生效。

直接执行 reIndex 会阻塞整个副本集写入
在 MongoDB 副本集中,reIndex 是一个全局排他操作:它会在执行节点上获取数据库级写锁(W),且该锁会传播到所有从节点——即使从节点只是重放 oplog,也会因等待锁释放而卡住同步。这意味着主节点写入暂停、从节点延迟飙升、心跳超时风险上升,严重时触发自动故障转移。
常见错误现象:replSetGetStatus 显示 optimeDate 滞后数分钟以上;应用报 WriteConflict 或连接超时;监控看到 globalLock.currentQueue.w 持续 > 0。
- 只在维护窗口期执行,且必须确认所有节点负载低、oplog 足够大(至少覆盖重建耗时)
- 不要对分片集群的单个 shard 执行
reIndex—— 它不会自动广播,但可能打乱 chunk 分布一致性 - 4.2+ 版本中,
reIndex已标记为 deprecated,官方明确建议用dropIndex+createIndex替代
dropIndex + createIndex 滚动重建更安全,但需手动控制顺序
滚动重建本质是分步替换:先删旧索引(不阻塞写入),再建新索引(后台建索引可选)。但必须确保“删”和“建”不在同一时刻跨节点错乱——比如从节点还没删完旧索引,主节点已建好新索引,会导致查询路由或唯一约束行为不一致。
使用场景:需要长期在线重建、索引字段变更(如从 {a: 1} 改为 {a: 1, b: 1})、或修复因版本升级导致的索引元数据损坏。
- 始终在主节点执行,且确认
writeConcern: {w: "majority"}已生效,保证 oplog 写入完整 - 删除前检查索引名是否一致:
db.collection.getIndexes(),避免误删_id或其他关键索引 - 新建时加
{background: true},否则仍会锁表;但 background 模式下无法使用unique: true(4.4+ 支持,但需注意兼容性) - 建完后务必验证:用
db.collection.explain("executionStats").find({a: 1})确认走的是新索引
副本集不同步状态下重建索引可能引发数据不一致
如果某个从节点已落后较多(比如因磁盘 IO 压力未追上 oplog),而你在主节点完成 dropIndex 后,该从节点在重放这条 oplog 时发现索引不存在,会静默跳过——但后续 createIndex 的 oplog 又会被执行,最终导致该节点多出一个“孤儿索引”,而主节点没有。
典型错误现象:同一个查询在主/从节点返回结果不一致;getIndexes 输出索引数量或字段不一致;备份恢复后出现 KeyNotFound 错误。
- 重建前运行
rs.printSlaveReplicationInfo(),确保所有从节点secondsBehindMaster - 对延迟高的节点,先临时提升其优先级或暂停写入,等追平后再操作
- 重建完成后,用
db.runCommand({listIndexes: "collection"})在每个节点单独比对输出,尤其关注key和name字段
真正麻烦的是隐藏依赖:应用代码、聚合管道、TTL 索引与重建顺序
很多索引不是孤立存在的。expireAfterSeconds 依赖特定字段存在;聚合中的 $lookup 可能隐式依赖被删索引的排序性能;甚至 ORM 的 save 方法内部会根据索引定义决定是否触发唯一校验。一旦重建顺序不对,这些逻辑会在某次写入时突然失败。
最容易被忽略的点:TTL 索引删除后,文档不会立刻过期,但新插入文档将不再受 TTL 控制——这个行为变化不会报错,只会让数据堆积。
- 重建前 grep 应用代码里所有
collection.createIndex和collection.ensureIndex调用,确认无硬编码依赖 - TTL 索引必须最后重建,且重建后观察
db.collection.stats().indexDetails中expiresAfterSeconds是否生效 - 如果集合启用了
collation,新建索引时必须显式指定相同 collation,否则排序/比较行为会变










