嵌套字段索引必须写全路径、大小写敏感、数组字段天然触发多键索引;不按规则建,IXSCAN根本不会出现,查得再慢你也看不出问题。

直接说结论:嵌套字段索引必须写全路径、大小写敏感、数组字段天然触发多键索引;不按规则建,IXSCAN根本不会出现,查得再慢你也看不出问题。
嵌套字段索引必须用完整点号路径
MongoDB 不理解对象结构,只认字符串路径。比如文档是 {"user": {"profile": {"age": 25}}},想加速查 user.profile.age,索引键就得严格写成 "user.profile.age" —— 写成 "user.profile" 或 "age" 都无效。
-
"User.Profile.Age"和"user.profile.age"是两个完全不同的索引键,大小写、点号位置差一点就白建 - 字段值为
null、缺失或数组时,该索引项会被跳过,不影响查询结果但可能降低命中率 - 超 10 层嵌套(如
"a.b.c.d.e.f.g.h.i.j.k")会让索引构建和更新开销明显上升,建议实测评估是否值得
数组字段索引要防膨胀,$elemMatch 必须复合且顺序一致
对数组内部字段建索引,路径里必须包含数组名本身,比如 {"users": [{"score": 95}]},索引就写 "users.score",不是 "users..score",也不是展开成顶层字段。
- 对数组字段建单字段索引(如
{"tags": 1})会为每个数组元素生成独立索引条目,容易导致索引体积暴涨、写入变慢,甚至报index key too large - 如果只用于过滤(如
{tags: "mongodb"}),用{"tags": 1}没问题;但若还要.sort({tags: 1}),才需要这个排序方向 -
$elemMatch查询必须匹配索引前缀:查{comments: {$elemMatch: {author: "x", votes: {$gt: 5}}}},索引就得是{"comments.author": 1, "comments.votes": 1},顺序不能颠倒,也不能只建其中一个 - 外层条件(如
{status: "published"})应放索引最前面:{"status": 1, "comments.author": 1, "comments.votes": 1}
复合索引里嵌套字段的顺序怎么排?
按 ESR 原则:等值字段 → 排序字段 → 范围字段。但一旦嵌套字段出现在范围操作(如 $gt、$regex)中,它之后的所有字段在该索引中就无法用于范围裁剪。
- 常见误配:查
{"status": "active", "user.profile.score": {$gte: 80}, "user.profile.tags": "vip"},却建索引{"user.profile.score": 1, "status": 1, "user.profile.tags": 1}——score是范围字段,后面两个字段实际失效 - 正确建法:
{"status": 1, "user.profile.tags": 1, "user.profile.score": 1},把等值字段放前,范围字段放最后 -
$in算等值匹配,不影响后续字段的索引能力;但$ne、$not会直接让索引退化
聚合管道里 $match 走不走索引?
只在最开头的 $match 阶段走,而且必须是“裸”谓词 —— 不能包在 $expr 里,也不能混用 $and/$or 包裹后丢失字段路径直连性。
-
db.coll.aggregate([{$match: {"user.profile.age": {$gt: 18}}}])可走索引 -
db.coll.aggregate([{$match: {$expr: {$gt: ["$user.profile.age", 18]}}}])绝对不走索引,$expr完全绕过索引引擎 - 验证是否命中:加
.explain("executionStats"),重点看executionStages.stage是否为IXSCAN,以及nReturned是否接近totalDocsExamined
真正容易被忽略的是:索引路径写错、$elemMatch 字段顺序错、$expr 误用这三处,它们不会报错,只会默默变慢——你得靠 explain 才能揪出来。

















