$and查询应建复合索引(等值字段在前、范围字段在后),$or查询需为每个子句单独建索引或满足前导字段一致才可能用复合索引;混合查询中$or部分必须独立走索引,务必用.explain()验证执行计划。

MongoDB 对 $and 和 $or 查询的索引使用机制完全不同,不能套用同一套设计逻辑。直接结论:对 $and 优先建复合索引;对 $or,要么给每个子句单独建索引,要么把所有子句字段塞进一个复合索引(但后者仅在特定结构下有效)。
复合索引如何支持 $and 查询
$and 查询本质是多个条件同时满足,MongoDB 可以用单个复合索引覆盖全部条件,前提是字段顺序和查询模式匹配。
- 例如查询
db.users.find({status: "active", role: "admin", created_at: {$gt: ISODate("2024-01-01")}}),推荐索引为{status: 1, role: 1, created_at: 1}—— 等值字段放前,范围字段放后 - 如果把
created_at放最前,索引就无法用于status和role的等值过滤,只会做范围扫描,效率暴跌 -
$and中的隐式写法(如{a: 1, b: 2})和显式写法({$and: [{a: 1}, {b: 2}]})在索引使用上完全等价 - 不要为每个字段单独建单字段索引再组合——MongoDB 不会自动合并多个单字段索引来服务一个
$and查询
$or 查询为什么不能靠一个复合索引解决
MongoDB 对 $or 的索引策略是“每个子句独立走索引”,不是整条 $or 表达式共用一个索引。这是关键区别。
- 查询
db.users.find({$or: [{email: "a@b.com"}, {phone: "123"}]}),需要分别在email和phone上有索引,否则其中一个分支会退化为全表扫描(COLLSCAN) - 建一个复合索引
{email: 1, phone: 1}对这个查询完全无效——MongoDB 不会用它去查phone单独出现的分支 - 例外情况:当
$or所有子句都包含相同前导字段时,比如{$or: [{a: 1, b: 2}, {a: 1, c: 3}]},复合索引{a: 1, b: 1, c: 1}才可能被部分利用(实际取决于优化器选择,不保证) - 执行
.explain("executionStats")后看executionStages.children,能清楚看到每个$or分支各自用了哪个索引或是否COLLSCAN
and + or 混合查询的索引陷阱
像 {$and: [{type: "order"}, {$or: [{state: "paid"}, {state: "shipped"}]}]} 这类常见结构,容易误以为建 {type: 1, state: 1} 就够了——其实不够。
- 这个查询等价于
type == "order" AND (state == "paid" OR state == "shipped"),MongoDB 会先用type索引缩小范围,再对结果集做state的内存过滤(即IXSCAN+COMPOUND或FETCH),不是高效索引驱动 - 真正高效的做法是:确保
state字段有独立索引(哪怕只是单字段{state: 1}),这样$or部分才能走索引;type字段也需索引,或与state组成复合索引(如{state: 1, type: 1}),但要注意字段顺序必须让$or子句能命中 - 如果
$or子句里还有其他字段(如{$or: [{state: "paid", amount: {$gt: 100}}, {state: "shipped"}]}),那就得考虑为每个子句单独建索引,或改用$in(当语义允许时)
验证索引是否真被用上
别信直觉,一定要用 .explain() 看真实执行路径。
- 重点看
winningPlan.stage:要是出现COLLSCAN,说明至少有一个分支没走索引 - 查
executionStages.children,确认每个$or分支的stage是IXSCAN还是COLLSCAN - 注意
indexName字段——它只显示主索引名,$or多分支时可能混合用了不同索引,得展开children才能看到全貌 - 用
db.collection.getIndexes()核对当前存在的索引,避免残留旧索引干扰判断
最常被忽略的一点:MongoDB 的查询优化器有时会选错索引,尤其在统计信息过期或数据分布倾斜时。即使你建了看似合理的索引,也可能被跳过——定期运行 db.collection.stats({scale: 1}) 查看文档数和索引大小,必要时手动触发 db.collection.reIndex() 更新统计信息。











