explain显示IXSCAN却实际COLLSCAN,是因为Plan Cache复用了过时的winningPlan;Index Filter是人工强制指定索引的指令式约束,不依赖缓存、不受数据变更影响,仅对find/count/distinct生效,且需严格匹配query shape。

为什么 explain 显示 IXSCAN 却实际在 COLLSCAN?
这不是查询写错了,也不是索引没建,而是 MongoDB 查询优化器复用了过时的 winningPlan。当数据分布突变(比如某字段重复值从 0.1% 涨到 95%)、索引被重建、或集合文档量激增后,原先缓存的执行计划可能已失效,但优化器仍强行复用——结果 explain("queryPlanner") 里 stage 写着 IXSCAN,executionStats.totalDocsExamined 却等于总文档数。
Index Filter 是什么,它和 Plan Cache 有什么区别?
indexFilterSet 是 MongoDB 提供的“强制索引策略开关”,它不依赖缓存,而是为特定 query shape 硬编码指定必须使用的索引。与 Plan Cache 不同:
– Plan Cache 是自动学习、可能过期的“历史最优”;
– Index Filter 是人工干预、长期生效的“指令式约束”。
它不会被数据变更影响,也不会因 clearPlanCache 清除,只会在你显式调用 db.runCommand({deleteIndexFilter: ...}) 时失效。
如何为 find 查询设置 Index Filter?
必须严格匹配 query shape(字段顺序、大小写、是否带 $eq),否则无效。步骤如下:
- 先确认目标索引名:
db.collection.getIndexes(),记下你要锁定的索引名,例如Tags_SN_1_OPTime_-1 - 构造完全一致的查询对象(不含投影、排序等额外操作):
{"Tags.SN": "QZ435698245"} - 执行命令(注意:sort 和 projection 不参与匹配,但 query 必须精确):
db.runCommand({ setIndexFilter: "duoduologmodel", query: {"Tags.SN": "QZ435698245"}, indexes: [{"Tags.SN": 1, "OPTime": -1}] }) - 验证是否生效:
db.duoduologmodel.find({"Tags.SN": "QZ435698245"}).explain("executionStats")中检查queryPlanner.indexFilterSet是否为true,且executionStats.executionStages.stage真为IXSCAN
容易忽略的关键限制
Index Filter 不是万能胶:
- 它只对
find、count、distinct生效,aggregate或updateMany不受约束 - 无法覆盖
$text或通配符索引场景 - 如果指定的
indexes数组中没有当前 query 能用的索引,MongoDB 会直接报错error: "no matching index",而不是退回到默认行为 - 多个 query shape 共享同一 filter 时,必须分别设置,不能通配
真正起作用的从来不是“加了索引”,而是“让这条查询每次都被迫走这个索引”。Index Filter 的价值,就在于把不确定的优化器决策,变成确定的执行路径控制。


















