部分索引只覆盖子集
部分索引通过过滤表达式只索引满足条件的文档:空间更小、写入维护成本更低,但查询必须包含与该条件兼容(更强或相等)的条件才能命中。它特别适合两类需求:一是「唯一约束只对特定状态生效」,二是「绝大多数查询都带同一个筛选条件」。
基本信息
| 定义 | createIndex({ email: 1 }, { partialFilterExpression: { status: "active" }, unique: true }) |
|---|---|
| 命中前提 | 查询条件必须蕴含过滤表达式(例如同时限定 status 为 active) |
| 典型用途 | 只在有效记录上做唯一约束、减少索引体积与写入放大 |
| 查看 | getIndexes() 的 partialFilterExpression 字段 |
| 不支持组合 | 不能与其他特殊索引选项随意叠加,交叉使用时需核对文档 |
| 适用版本 | 3.2 及以上 |
典型使用场景
- 软删除场景:只对未删除的记录建索引,历史数据不占索引空间
- 状态约束:同一邮箱允许存在多条历史记录,但有效记录必须唯一
- 多租户表:只为活跃租户的近期数据建索引,控制索引规模
- 大字段过滤:只索引有值的那部分文档,减少空值造成的索引膨胀
- 热点集合瘦身:过滤条件长期固定的查询,用部分索引替代全量复合索引
示例
示例
// 唯一约束只作用于活跃记录:历史记录即使邮箱相同也不冲突
db.users.createIndex(
{ email: 1 },
{ partialFilterExpression: { status: "active" }, unique: true }
)
// 查询必须带上兼容的条件,否则索引不会被采用
db.users.find({ email: "a@example.com", status: "active" }).explain("executionStats")
// 查看索引的过滤表达式,确认与查询条件一致
// db.users.getIndexes().filter(function (ix) { return ix.partialFilterExpression })命中判断读法
| 索引过滤条件 | 查询条件 | 是否命中 |
|---|---|---|
| status = active | status = active 且有其他条件 | 命中 |
| status = active | 只按其他字段查询 | 不命中,退回其他索引或扫描 |
| status 属于枚举集合 | status 取同集合中的单个值 | 命中 |
| 不写过滤条件(全量索引) | 任意条件 | 命中但索引更大、写入更重 |
用部分索引表达「唯一约束只对特定状态生效」,比全量唯一索引更贴合业务:历史数据与已删除记录不会再与当前有效数据抢同一个唯一位置。
过滤条件与查询条件不满足蕴含关系时,索引会被完全跳过:上线前用 explain 逐个核对核心查询,避免建了索引却没人用。