稀疏索引适用于字段在60%以上文档中完全缺失的场景,如backup_phone;它跳过字段不存在的文档,但不跳过值为null的文档;与唯一索引组合时仅约束存在该字段的文档,分片集群下无法保障全局唯一。

稀疏索引(sparse: true)什么时候该用?
当字段在 60% 以上的文档中缺失时,稀疏索引才值得考虑。它不是“只要字段可选就加”,而是针对真正稀疏的业务字段——比如 backup_phone、middle_name、referral_code 这类只在特定用户场景下存在的字段。
常见误用:给 status 字段建稀疏索引,但其实所有文档都有这个字段(哪怕值为 null 或 "pending"),这时稀疏索引不仅没收益,还会让查询计划更不可控。
- 稀疏索引跳过的是「字段完全不存在」的文档,不是值为
null的文档 - 如果字段存在但值为
null,该文档仍会被索引(除非你额外加partialFilterExpression过滤) - 用
db.collection.stats()查看字段覆盖率,比凭经验判断更可靠
稀疏索引 + 唯一约束怎么写才安全?
直接组合 sparse: true 和 unique: true 是合法语法,但必须清楚它的行为边界:它只对「字段存在」的文档 enforce 唯一性,不存该字段的文档可以无限插入。
典型错误是以为这样能实现“全局唯一 + 可选字段”的效果,结果发现大量无该字段的文档堆积,后续业务逻辑出错。
- 正确写法:
db.users.createIndex({ email: 1 }, { unique: true, sparse: true }) - 插入
{ name: "alice" }允许;插入{ name: "bob", email: "bob@example.com" }也允许;但再插一个{ name: "charlie", email: "bob@example.com" }会报E11000 duplicate key error - 分片集群下,这种索引不能作为全局唯一保障——它只在单个分片内有效,除非
email同时是分片键
为什么查询没走稀疏索引?
MongoDB 默认拒绝用稀疏索引执行可能返回不完整结果的查询。比如 find({ score: { $lt: 90 } }),如果集合里有文档不含 score 字段,而稀疏索引根本不包含它们,MongoDB 就不会选这个索引,哪怕它看起来“更匹配”。
这不是 bug,是设计选择:宁可慢一点(全表扫描),也不返回漏数据的结果。
- 用
.explain("executionStats")看winningPlan.stage,如果是COLLSCAN而不是IXSCAN,大概率就是这个原因 - 强制使用要加
.hint(),但必须确认业务能接受缺失字段文档被排除在外 - 排序场景更危险:
sort({ score: 1 })若用稀疏索引,结果集一定缺掉所有无score的文档
稀疏索引 vs 部分索引,现在该选哪个?
部分索引(partialFilterExpression)是稀疏索引的超集,从 MongoDB 3.2 起就推荐优先使用它。稀疏索引本质只是 { field: { $exists: true } } 的语法糖,而部分索引能表达更精确的条件。
比如你想只为已激活用户建索引:{ status: "active" },或者只为非空且大于 0 的评分建索引:{ score: { $gt: 0 } }——这些都只能用部分索引。
- 创建部分索引:
db.orders.createIndex({ amount: 1 }, { partialFilterExpression: { status: "paid" } }) - 部分索引默认具备稀疏语义(跳过不满足表达式的文档),且支持更多过滤逻辑
- 注意:部分索引的过滤表达式必须是确定性的,不能含
$currentDate、$expr等运行时变量
真正需要稀疏索引的只剩一种情况:你用的是 MongoDB 3.0 或更早版本,或者团队约定死守旧语法。否则,直接上部分索引。


















