分片集群索引损坏需逐shard诊断修复:登录各shard主节点运行validate({full:true})定位问题,重建时确保分片键为前导、停写、调大maxIndexBuildMemoryUsageMegabytes并显式指定background:true。

不能直接在 mongos 上执行 createIndex 并指望它“快速修好”——分片集群里索引损坏必须逐 shard 诊断、逐 shard 恢复,否则会漏建、错建、甚至让部分 shard 退化为全表扫描。
先确认是哪个 shard 的索引坏了
分片集群中,db.collection.validate() 在 mongos 层返回的结果是汇总值,无法定位到具体 shard。你得登录每个 shard 的 primary 节点(不是 config server,也不是 mongos),单独运行验证:
- 查出所有 shard:运行
sh.status(),记下每个 shard 名(如shard01)和对应 mongod 的地址 - 用
mongo --host <shard-primary-host>:27017登录该 shard 的主节点 - 执行
db.getSiblingDB("your_db").collection.validate({full: true}),重点看valid和errors字段;若含Invalid index entry或Index key mismatch,说明该 shard 索引已损坏
重建前必须停写并检查分片键约束
在单个 shard 上重建索引看似独立,但如果你的查询依赖分片键字段,而新建的索引没包含它或顺序不对,mongos 就不会下推查询,性能反而崩掉:
- 确认原索引定义:在 mongos 上运行
db.collection.getIndexes(),抄下字段顺序、方向(1或-1)、是否稀疏/唯一等选项 - 辅助索引必须以分片键字段为前导:比如分片键是
{tenant_id: 1, created_at: -1},那修复用的索引就得是{tenant_id: 1, status: 1},不能是{status: 1, tenant_id: 1} - 重建期间禁止对该 shard 写入:用
rs.stepDown()临时降权该 shard 的 primary,或直接在应用层切走流量;别信“只读就够了”,background: true仍可能与 chunk 迁移冲突
调高内存 + 强制后台构建,但别并发太多
MongoDB 4.2 默认对 background: true 索引构建限制内存为 128MB,千万级集合上这会导致 I/O 密集、耗时翻倍。必须在每个目标 shard 的 mongod 进程上单独调参:
- 动态设置(无需重启):
db.adminCommand({ setParameter: 1, maxIndexBuildMemoryUsageMegabytes: 2048 })—— 值设为 2048 表示 2GB,适用于 32GB 内存的 shard 节点 - 该参数只作用于单个构建进程,且仅对
background: true生效;前台构建不认这个参数 - 注意并发上限:
maxNumActiveUserIndexBuilds默认是 3,如果你同时在 3 个 shard 上建索引,它们会排队;想并行建,得同步调大这个值,但每多一个并发就多占一份内存(比如设为 6,就要预留 12GB 空闲物理内存) - 命令示例:
db.collection.createIndex({tenant_id: 1, status: 1}, {background: true})—— 必须显式加background: true,否则即使调了内存也无效
restore 后索引失效?别急着建,先跳过索引导入
如果索引损坏发生在 mongorestore 之后,大概率是流程错了:直接 restore 并带 --drop 会先删数据再建索引,但此时 oplog 还没追平、WiredTiger 缓存混乱,极易触发索引元数据错位。
- 正确做法是:先用
mongorestore --noIndexRestore导入数据,确保所有 shard 数据一致 - 再统一在每个 shard 的 primary 上执行
createIndex,全部完成后,用db.collection.getIndexes()逐个核对字段、顺序、选项是否完全一致 - 最后跑一次
validate({full: true}),确认valid: true且errors为空;别省略这步——有些损坏只有 full 模式才能暴露
最易被忽略的是:分片集群里没有“全局索引”这回事,每个 shard 的索引状态彼此隔离。你以为建完了,其实某个 shard 因磁盘满没响应,mongos 不报错,但后续查询落到那个 shard 就慢得离谱。务必登录每个 shard 主节点亲自验证,而不是只信 mongos 返回的 OK。

















