
复合索引字段顺序必须和 sort() 顺序严格一致
MongoDB 的 sort() 要走索引加速,复合索引的字段排列顺序必须与排序字段的顺序、方向(升/降)完全匹配。比如你执行 db.collection.find().sort({ status: 1, createdAt: -1, priority: 1 }),那索引必须是 { status: 1, createdAt: -1, priority: 1 } —— 换任何一个字段位置或方向,MongoDB 就无法用该索引完成完整排序,可能回退到内存排序(触发 Sort stage),甚至报错 Executor error during find command: Sort exceeded memory limit。
常见错误现象:
索引建成了 { status: 1, priority: 1, createdAt: -1 },但 sort() 是按 status, createdAt, priority,结果不走索引索引是 { status: 1, createdAt: 1 },但 sort() 写了 { status: 1, createdAt: -1 },方向不一致导致索引失效
升序/降序混合时,索引方向不能随意省略
MongoDB 不会把 1 当作默认值自动补全——哪怕你只对最后一个字段指定 -1,前面所有字段也必须显式声明方向。例如:
db.collection.createIndex({ status: 1, createdAt: -1, priority: 1 })
不能写成:
db.collection.createIndex({ status: 1, createdAt: -1, priority: 1 }) // ✅ 正确db.collection.createIndex({ status: 1, createdAt: -1, priority: 1 }) // ❌ 错误示例(实际无效)
使用场景:后台任务列表按状态(active 优先)、创建时间(最新在前)、再按优先级(高优先)排序;这类多维业务排序必须靠方向明确的复合索引支撑。
性能影响:方向不一致时,即使前缀匹配,MongoDB 也无法利用索引后缀字段做排序,只能扫描+内存排序,数据量稍大(如 >10MB)就容易 OOM。
排序字段包含查询条件时,索引要兼顾 filter + sort
如果查询带 find({ status: "active" }) 并且 sort({ createdAt: -1, priority: 1 }),最优索引不是只覆盖排序字段,而是把等值查询字段放最前,再接排序字段:
{ status: 1, createdAt: -1, priority: 1 } ✅ 可同时用于过滤 + 排序{ createdAt: -1, priority: 1 } ❌ 无法加速 status 查询,filter 阶段全表扫
注意:范围查询字段(如 { updatedAt: { $gt: ISODate(…) } })不能放在等值字段之后再接排序字段——它会截断索引的有效排序部分。例如 { status: 1, updatedAt: { $gt: … }, createdAt: -1 } 中,createdAt 实际无法被用于排序加速。
排序稳定性依赖 _id 或唯一字段兜底
MongoDB 本身不保证「完全相同排序键」的文档返回顺序稳定——除非你在 sort() 中加入唯一字段(如 _id)作为最终决胜字段。例如:
db.collection.find({ status: "active" }).sort({ status: 1, createdAt: -1, _id: 1 })
否则,当多个文档的 status 和 createdAt 完全相同时,不同查询、不同 mongod 版本、甚至主从同步延迟都可能导致顺序抖动。这在分页(skip/limit)场景下尤其危险,容易漏数据或重复。
容易被忽略的地方:很多人以为加了复合索引就“稳了”,其实没加 _id 或其他唯一字段兜底,排序结果仍是非确定性的——即使当前看着有序,也不能当作业务逻辑依赖。

评论(0)