索引碎片的真实信号是相同查询executionTimeMillis波动剧烈而totalKeysExamined和totalDocsExamined稳定;若totalDocsExamined/nReturned>50则为索引未覆盖而非碎片。

确认是否真为索引碎片引发慢查询
直接运行 db.collection.explain("executionStats").find({your_query}),重点观察 【totalDocsExamined 远大于 nReturned,且 stage 出现大量 IXSCAN + FETCH 组合】;若 totalDocsExamined / nReturned > 50,基本可排除碎片,实为索引未覆盖——碎片本身不导致 IXSCAN 后大量 FETCH,它只影响索引页的物理连续性与内存加载效率。
索引碎片的真实信号是:相同查询在不同时间执行,executionTimeMillis 波动剧烈(如 12ms → 280ms → 45ms),且 totalKeysExamined 和 totalDocsExamined 数值稳定、无异常放大。此时才需进入碎片排查流程。
检查索引碎片程度
方法一:使用 db.collection.stats() 查看基础指标
执行 db.orders.stats({scale: 1024})(单位换算为 KB),重点关注 indexCount、indexSize 和 avgObjSize;若 indexSize / (indexCount * avgObjSize) > 3.5,说明索引数据页空洞严重,存在明显碎片。
方法二:调用 $indexStats 获取实时页级分布
运行 db.orders.aggregate([{$indexStats: {}}, {$match: {name: "idx_status_createdAt"}}]),检查返回中的 accesses.ops 与 accesses.since;若 ops 极低但 since 时间很长,表明该索引长期未被有效利用,写入膨胀后易残留碎片。
重建索引以清除碎片
第一步:确认当前索引名
db.orders.getIndexes() 找出目标索引的完整 name 字段,例如 "idx_status_createdAt"。
第二步:后台重建(生产环境必须)
db.orders.reIndex() 会阻塞所有读写,【绝对禁止在业务高峰期执行】;正确做法是逐个重建:db.orders.dropIndex("idx_status_createdAt") → db.orders.createIndex({"status": 1, "createdAt": -1}, {background: true, name: "idx_status_createdAt"})。
第三步:验证重建效果
重建完成后立即执行 db.orders.stats({scale: 1024}),对比 indexSize 是否下降 15% 以上;再跑一次原查询的 .explain("executionStats"),确认 executionTimeMillis 波动收敛至 ±5ms 范围内。











