部分索引能省空间是因为只索引满足partialFilterExpression条件的文档子集,跳过低频或无关数据,显著减少索引条目数;其过滤表达式必须与查询条件严格对齐且仅支持等值、$exists、$gt等确定性操作符,不支持$or、$regex等。

部分索引为什么能省空间?
因为 MongoDB 的索引大小 ≈ 索引条目数 × 单条记录平均体积。如果你的集合有 1 亿条文档,但只有最近 7 天的订单(约 500 万条)被高频查询,而你建了全量 {user_id: 1, created_at: -1} 索引,那另外 9500 万条冷数据也会生成索引条目——占磁盘、拖写入、压内存。部分索引通过 partialFilterExpression 直接跳过这些低频文档,只索引真正需要的子集。
怎么写有效的 partialFilterExpression?
必须和真实查询条件对齐,否则索引会被跳过;也不能用 MongoDB 不支持的操作符。常见可用写法包括:
-
{status: "paid"}—— 等值匹配,最常用 -
{created_at: {$gt: ISODate("2024-06-01T00:00:00Z")}}—— 时间范围,注意字段必须是 BSONDate类型,字符串日期(如"2024-06-01")无效 -
{$and: [{status: "paid"}, {created_at: {$gt: ISODate("2024-06-01T00:00:00Z")}}]}—— 显式$and,仅允许在顶层使用 -
{email: {$exists: true}}—— 替代稀疏索引,且更可控
禁止写法:$or、$regex、$text、$in(除非配合等值)、引用不存在字段(创建直接失败)。
创建后怎么验证它真在起作用?
不能只看 createIndex 返回 ok: 1 就算完。关键验证点有三个:
- 查索引大小:
db.orders.stats().indexDetails["idx_user_paid_recent"].size,对比全量索引,下降 85%+ 才算有效 - 确认查询命中:
db.orders.find({user_id: 123, status: "paid", created_at: {$gt: ISODate("2024-06-01T00:00:00Z")}}).explain("executionStats"),看executionStats.totalKeysExamined是否明显小于全量索引下的扫描数 - 反向验证跳过:
db.orders.find({user_id: 123, status: "pending"}).explain(),应看到执行计划里没走IXSCAN,而是COLLSCAN或其他索引
部分索引 vs 稀疏索引,选哪个?
优先用部分索引。稀疏索引只认“字段是否存在”,能力有限;而部分索引是它的超集:{field: {$exists: true}} 就能完全替代 sparse: true,还能叠加时间、状态等多维条件。唯一例外是当你需要 {$exists: false} 查询时——部分索引无法覆盖,这时稀疏索引反而可能更合适(但要注意性能风险)。
最容易被忽略的一点:部分索引的过滤表达式不是“优化提示”,而是硬性契约。只要查询条件没包含或没隐含满足 partialFilterExpression,MongoDB 就绝不会用它——哪怕看起来“差不多”。


















