布尔字段直接建单字段索引通常不值得:因区分度低,MongoDB优化器多跳过索引而全表扫描;仅当与高选择性字段组合查询时,宜作为复合索引的一部分。

布尔字段直接建索引通常不值得
MongoDB 对 boolean 字段建普通单字段索引,多数情况下不会带来明显查询加速,反而增加写入开销和内存占用。因为只有两个值(true/false),B-tree 索引的区分度极低,优化器大概率会跳过它,改走 collection scan。
常见错误现象:explain() 显示 executionStats.executionStages.stage: "COLLSCAN",即使字段上有索引;或者 indexBounds 范围极大(如 [false, true]),实际等效全扫。
- 适用场景仅限:该布尔字段常与高选择性字段组合过滤,且查询模式高度固定(例如
{status: true, createdAt: {$gt: ...}}) - 更优解是把它作为复合索引的「前导字段」或「尾部字段」,取决于查询中是否带范围/排序
- 如果只查
{flag: true}且数据中true占比
低基数枚举字段用部分索引比普通索引更有效
比如 status 字段只有 "pending"、"processing"、"done" 三个值,建普通索引意义不大;但若业务中 90% 查询都是 {status: "done"},那针对这个值建部分索引就非常划算。
实操建议:
- 用
db.collection.createIndex({status: 1}, {partialFilterExpression: {status: "done"}})创建部分索引 - 确保
partialFilterExpression中的值是精确匹配(不能用正则或 $in),否则 MongoDB 不会使用该索引 - 注意:部分索引不包含被过滤掉的文档,所以
count()或聚合中若没显式排除,结果可能和全量不一致 - 多个高频枚举值可以分别建多个部分索引,但别贪多——每个额外索引都增加写放大
复合索引里布尔/枚举字段放哪一位很关键
顺序决定索引能否命中。MongoDB 只能高效使用「前缀连续」的字段组合。比如索引 {isDeleted: 1, createdAt: -1, userId: 1},以下查询能用上:
db.logs.find({isDeleted: false, createdAt: {$gt: ISODate("...")}}).sort({createdAt: -1})
但这个不行:
db.logs.find({createdAt: {$gt: ISODate("...")}, userId: "u123"})
原因:缺少前导字段 isDeleted,索引前缀断裂。
- 如果布尔/枚举字段总是出现在查询条件里(例如所有 API 都带
isArchived: false),适合放最左位 - 如果它只是可选过滤项(有时有,有时没有),而其他字段选择性更高,就把它挪到右边,避免拖累整个索引效率
- 排序字段必须紧跟在用于等值匹配的字段之后;否则索引无法支持排序,触发内存 sort 或拒绝执行
验证索引是否真生效,别只看 executionStats
explain("executionStats") 里 nReturned 小、totalDocsExamined 大,说明索引没起作用;但反过来也不绝对——有时索引用了,但因基数太低,仍要扫描大量键。
- 重点看
executionStats.executionStages.indexBounds:如果是{"isPublic": [["true", "true"]]},说明精准命中;如果是[["false", "true"]],基本等于没用 - 用
db.collection.getIndexes()检查索引定义是否和查询模式对齐,尤其注意大小写、字符串 vs 布尔类型("true"≠true) - 生产环境别只测单条查询,用
mongostat或慢日志观察idxCount和scanAndOrder指标变化
真正难的是平衡:既要覆盖高频查询路径,又要控制索引总数。一个集合超过 5 个索引,写性能下降就很明显了——这时候就得砍掉那些“看起来有用但实际从不被选中”的索引。

















