必须分批错峰构建索引:先确认副本集状态正常,禁用从节点自动建索引,再逐节点后台创建并监控oplog与执行时间,超时或异常立即熔断,最后验证执行计划。

在MongoDB副本集上为数十亿文档的超大集合构建索引时,若直接前台执行会导致主节点写入阻塞、oplog堆积、从节点复制延迟飙升甚至failover;必须分批控制构建节奏,确保每一步都可观察、可中断、不干扰业务读写。
确认副本集拓扑与当前状态
连接任意节点(推荐直连Primary),运行 rs.status(),确认所有成员状态为 PRIMARY 或 SECONDARY,且 optimeDate 与主节点差距小于5秒。若存在 STARTUP2、RECOVERING 或 ROLLBACK 状态节点,暂停操作——【索引构建必须在全部数据节点同步完成初始同步后进行】。
再执行 db.adminCommand({ "replSetGetStatus": 1 }),检查 members[n].health 全为1,并记录每个节点的 name(如 mongo1:27017)和 stateStr。
禁用自动索引构建传播
在mongos或任意mongod中执行以下命令,关闭默认的“主建索引→从节点自动重放”行为:
db.adminCommand({ setParameter: 1, disableIndexBuildsOnSecondaries: true })
该参数仅影响新发起的索引构建任务,不会中断已在运行的后台索引。它让后续所有 createIndex() 命令只在当前连接节点生效,避免Secondary被意外拖入高负载。注意:此设置需在每个要建索引的节点上单独执行,不能通过mongos下发。
分片键无关索引:逐节点错峰构建
适用于非分片键字段(如 {status: 1, createdAt: -1})的复合索引。必须人工拆解为单节点任务,不可依赖mongos统一调度。
方法一:按节点顺序依次构建
① 直连Primary节点:mongo --host mongo1:27017 -u admin -p pwd --authenticationDatabase admin
② 在该节点执行:db.getSiblingDB("mydb").orders.createIndex({status: 1, createdAt: -1}, {background: true, maxTimeMS: 18000000})(3小时超时)
③ 等待命令返回 "ok" : 1 后,立即检查:db.getSiblingDB("mydb").orders.getIndexes(),确认新索引 background: true 且 state: "READY";同时运行 db.currentOp({ "secs_running": { "$gt": 60 }, "secs_running": { "$lt": 300 } }),确认无长时锁等待。
④ 断开连接,直连第一个Secondary:mongo --host mongo2:27017 -u admin -p pwd --authenticationDatabase admin,重复步骤②③
⑤ 最后直连第二个Secondary,同样执行。每步之间至少间隔15分钟,给oplog追赶留出缓冲窗口。
方法二:利用partialFilterExpression压缩索引体积
若业务查询总是带前缀条件(如 {tenant_id: "org_123", status: "pending"}),直接建全量索引是浪费。改用部分索引:db.orders.createIndex({status: 1, createdAt: -1}, {partialFilterExpression: {tenant_id: "org_123"}, background: true})。这能将索引大小压缩90%以上,构建时间从数小时降至十几分钟——【partialFilterExpression必须精确匹配高频查询模式,否则索引无法命中】。
监控索引构建进度与风险熔断
在每个节点构建期间,持续轮询以下两个命令:
db.currentOp({ "secs_running": { "$gt": 300 }, "command.createIndexes": { "$exists": true } })
db.getSiblingDB("local").oplog.rs.stats().size / 1024 / 1024(查看oplog实时增长MB/s)
若发现某节点 secs_running > 1800 且 oplog.rs 写入速率持续高于5MB/s,立即终止该节点索引:db.killOp(<opid>)</opid>。不要等待超时——长时间运行的后台索引可能已卡在磁盘I/O或内存碎片阶段,继续等待只会加剧从节点落后。
构建完成后,对每个节点单独验证执行计划:db.orders.explain("executionStats").find({status: "pending"}).hint("status_1_createdAt_-1"),确认 stage 为 IXSCAN 且 nReturned 与预期一致。


















