索引设计需遵循esr规则:等值字段最前、排序字段居中、范围字段最后;覆盖索引要求filter和projection全部命中索引字段且不含未索引字段;应定期清理零访问或低频索引以减少写放大和内存浪费。

索引不是越多越好,也不是越宽越好——它本质是用写入开销和内存换查询速度。关键在“精准匹配查询模式”,而不是堆索引。
复合索引字段顺序必须遵循 ESR 规则
等值查询字段(Equality)放最前,排序字段(Sort)居中,范围查询字段(Range)放最后。顺序错,索引就可能失效。
-
status:1是等值条件(如{status: "done"}),应排第一位 -
createdAt:-1是排序字段(如.sort({createdAt: -1})),排第二位 -
amount:1是范围条件(如{amount: {$gt: 100}}),只能放第三位 - 错误示例:
db.orders.createIndex({amount:1, status:1, createdAt:-1})—— 范围字段打头,后续字段无法被索引利用
覆盖索引能省下大量内存与IO,但只对特定投影有效
覆盖索引要求查询的 filter 和 projection 全部命中索引字段,且不包含 _id(除非显式包含)。一旦加了未索引字段,就会回表读文档,失去覆盖意义。
- 可行:
db.users.createIndex({email:1, name:1, role:1})+find({email:"a@b.com"}, {name:1, role:1, _id:0}) - 不可行:
find({email:"a@b.com"}, {name:1, avatarUrl:1})——avatarUrl不在索引里,必须加载文档,缓存压力翻倍 - 注意:
_id默认返回,若不需要,务必加_id:0,否则即使其他字段都覆盖,也会触发文档读取
定期清理无效索引,避免写放大和内存浪费
MongoDB 每个索引都会增加写操作开销(插入/更新/删除都要同步更新索引),并占用 WiredTiger 缓存。长期不用的索引,就是白占内存的“幽灵索引”。
- 查当前索引使用情况:
db.collection.aggregate([{$indexStats:{}}, {$group:{_id:"$name", accesses:{$sum:"$accesses"}}}]) - 零访问或低频访问(如
accesses / 天)的索引,优先评估是否可删 - 删除前确认:
db.collection.getIndexes()看索引名,再用db.collection.dropIndex("idx_name") - 特别警惕:单字段索引和复合索引字段前缀重叠时(如已有
{a:1,b:1},又建了{a:1}),后者通常冗余
真正难的不是建索引,而是判断“这个查询到底该走哪个索引”。explain("executionStats") 返回的 keysExamined 和 docsExamined 差距过大(比如 keysExamined=5000、nreturned=2),说明索引没剪枝能力——这时候调顺序、加字段、删冗余,比加内存更治本。











