ESR顺序不可调换,因MongoDB索引匹配依赖前缀匹配机制:等值字段必须最左以快速定位,排序字段居中避免内存排序,范围字段置右才能使后续字段被索引覆盖;顺序错则无法命中索引前缀,导致COLLSCAN。

复合索引按 ESR 顺序建,是解决大数据量查询慢最直接有效的手段——但顺序错一个字段,索引就可能完全失效。
为什么ESR顺序不能调换?
ESR(Equality → Sort → Range)不是建议,而是 MongoDB 索引匹配的硬性规则。它决定了查询条件能否命中索引前缀,进而决定是否触发 IXSCAN 而非 COLLSCAN。
- 等值字段(
status: "active")必须放最左,否则无法利用索引做快速定位 - 排序字段(
join_date: -1)紧随其后,才能避免内存排序(SORTstage) - 范围字段(
age: {$gt: 18})必须放最后,否则后续字段无法被索引覆盖 - 反例:
db.users.createIndex({age:1, status:1, join_date:-1})对{status:"active", age:{$gt:18}}查询几乎无效
如何验证索引是否真正生效?
别只看有没有索引,要看执行时到底扫了多少键和文档。用 explain("executionStats") 是唯一可靠方式。
- 关键指标必须同时满足:
stage是IXSCAN、totalKeysExamined≈nReturned、totalDocsExamined接近 0 - 如果
totalDocsExamined很大(比如 10 万+),说明仍需回表读文档,索引未覆盖查询字段 - 如果
executionStats.executionTimeMillis> 500ms,即使用了索引,也可能因字段顺序或选择性差导致效率低下
哪些场景下ESR会失效?
ESR 是理想路径,但业务查询常有例外,这些点容易被忽略:
- 使用
$or或$ne:MongoDB 通常放弃索引走全表扫描,应改写为$in+ 复合条件 - 排序方向不一致:索引是
{a:1, b:-1},但查询.sort({a:1, b:1})就无法利用 b 字段排序 - 查询中跳过中间字段:索引
{a:1, b:1, c:1}可支持{a:1}和{a:1,b:1},但{a:1,c:1}不会使用 c 字段索引(最左前缀失效) - 字符串字段带 collation:索引创建时指定了
{collation:{locale:"zh"}},查询就必须显式加.collation({locale:"zh"}),否则不命中
复合索引字段数量与性能的平衡点
字段越多,覆盖能力越强,但写入开销和内存占用也线性上升。实际中超过 4 个字段的复合索引极少必要。
- 优先合并高频组合查询,而非“所有可能字段都塞进去”
- 单个索引最多支持 32 个字段,但集合总索引数不能超 64 个,每个索引键长不能超 1024 字节
- 对
text、2dsphere等特殊类型字段,不能和其他字段混在同一个复合索引里 - 部分索引(
partialFilterExpression)可大幅减小体积:例如只对{status:"active"}建索引,体积常缩小 70% 以上
ESR 不是魔法公式,它依赖你准确识别查询中的等值、排序、范围三类条件——而真实日志里,往往要反复比对 explain 输出和原始查询语句,才能确认哪一环断了。











