mongodb动态过滤器性能取决于索引匹配度;必须根据explain()中nreturned与totaldocsexamined比值及executionstages.stage(如collscan需优化)判断索引有效性,并按等值→范围→数组顺序创建复合索引,且需随查询变化持续调优。

MongoDB 动态过滤器本身不会自动变快,响应速度取决于你是否为实际查询路径建立了匹配的索引——没有索引支撑的 $and/$or/$in 组合,哪怕只查 10 万条数据也会明显卡顿。
哪些字段必须建索引?看 explain() 的 executionStats 里 nReturned 和 totalDocsExamined 的比值
动态过滤器常表现为用户勾选多个条件(如 status: "active", category: { $in: ["A", "B"] }, createdAt: { $gte: ISODate("...") }),但真正影响性能的是「每次查询实际命中哪些字段组合」。别猜,用 explain("executionStats") 看真实行为:
- 如果
totalDocsExamined远大于nReturned(比如 5000 vs 12),说明扫描了大量无关文档,当前索引没覆盖到关键筛选字段 - 重点盯
executionStages.stage:出现COLLSCAN就是全表扫,必须优化;IXSCAN才是走索引 -
indexName字段为空或不是你预期的复合索引名,说明查询没用上你建的索引
compound index 的字段顺序不能按「过滤频率」排,而要按「等值 → 范围 → 数组/排序」的严格顺序
例如用户常组合查询 tenantId(等值)、status(等值)、createdAt(范围)、tags($in 或 $elemMatch),正确索引是:
db.collection.createIndex({ tenantId: 1, status: 1, createdAt: 1 })
而不是把 createdAt 放前面——MongoDB 只能高效使用索引前缀做等值匹配,一旦遇到范围查询($gt, $gte, $lt),后续字段就无法用于索引过滤。常见错误:
- 把
$in字段放在范围字段之后:索引{ a: 1, b: 1, c: 1 }中,{ a: 1, b: { $in: [...] }, c: { $gt: ... } }只能用上a和b,c不生效 - 对数组字段建普通索引后直接
$in多个值:需确认是否已建多键索引(MongoDB 自动处理),否则可能漏匹配 - 忽略
tenantId这类租户隔离字段:没它,所有租户数据混查,索引失效风险极高
用 $expr 或聚合管道里的 $facet 做动态逻辑时,索引基本失效,得提前物化中间状态
当过滤条件含运行时计算(如 { $expr: { $gt: [{ $subtract: ["$updatedAt", "$createdAt"] }, 3600000] } })或需要多路聚合再合并(如按不同标签分别统计再取并集),MongoDB 无法利用常规索引加速。此时应:
- 把计算结果存成新字段(如
durationMs),并为该字段建索引,查询改用{ durationMs: { $gt: 3600000 } } - 避免在高频接口中用
$facet实现“多条件并行统计”,改为前端分多次请求,或后台预生成常用维度的汇总集合(如按小时/状态预聚合) - 慎用
$text索引替代结构化过滤:全文索引不支持范围、不精确匹配,且无法和其他字段高效组合
最常被忽略的一点:索引不是建完就一劳永逸。当新增一个高频组合查询(比如突然多了按 region + priority + dueDate 查的任务),旧索引很可能完全不适用——得重新跑 explain(),观察执行计划,再决定是否加新索引或调整字段顺序。











