MongoDB正则索引仅支持^开头的字符串形式前缀匹配,复合索引中范围查询后字段失效,类型变更导致旧索引无法匹配新值,低区分度前缀使索引低效。

正则表达式没用^开头,索引直接失效
MongoDB只对前缀固定的正则能走索引,也就是必须以^开头、不带i等标志、且用字符串形式写。比如{ name: { $regex: "^Alice" } }可能命中索引;而{ name: { $regex: "Alice" } }或{ name: { $regex: /Alice/i } }都会退化为COLLSCAN。
常见错误包括:用JS字面量/^Alice/代替字符串写法,或加$options: "i"——哪怕字段有索引,这两种写法都绕过索引。
- 必须用
$regex: "^xxx",不能用$regex: /^xxx/ -
$options一出现(哪怕只是"i"),索引就失效 - 复合索引中,该字段必须是第一个,否则前缀匹配也无效
查询条件超出索引有效前缀范围
在复合索引{ a: 1, b: 1, c: 1 }上,如果查{ a: 1, b: { $gt: 2 }, c: 3 },那c字段根本不会参与索引过滤——因为b用了范围操作符($gt、$lt、$in、$ne),后续字段全部“断链”。
这不是MongoDB bug,而是B-tree索引的固有限制:范围查询之后的字段无法利用有序性。
- 等值字段尽量放前面,范围字段往后挪
- 想让
c也生效?把索引改成{ a: 1, c: 1, b: 1 },前提是c能做等值匹配 -
$not和$ne也属于范围语义,同样会截断后续字段
字段类型变更后旧索引残留
用$set把{"status": "1"}改成{"status": 1},表面看只是字符串变数字,但BSON类型变了。旧索引项仍按字符串编码,新值无法匹配,查询引擎只能全表扫。
explain()里会看到IXSCAN消失,executionStages.stage变成COLLSCAN。
- 改类型前先跑
db.collection.getIndexes()确认是否被索引覆盖 - 必须先
dropIndex(),再更新数据,最后createIndex() - 复合索引更敏感:哪怕只改了其中一个字段的类型,整个索引都失效
前缀区分度太低,索引形同虚设
就算explain()显示走了IXSCAN,也不代表快。比如查^A,但80%的用户名都以A开头,那索引扫描后还得逐个反查文档内容做二次匹配,nDocsExamined接近总数,实际比全表扫描还慢。
这时候索引不是没生效,而是“低效生效”——它扫了大量无关文档,I/O和CPU压力反而更大。
- 盯紧
executionStats.nDocsExamined和nReturned的比值 - 比值接近1 → 前缀太宽泛,换更长前缀(如
"^AliceW")或加其他筛选条件 - 高基数字段(如国家码
^CN、状态字段^active)特别容易踩这个坑
IXSCAN出现在执行计划里,只说明MongoDB尝试用了索引,不代表它过滤有效——得看nDocsExamined有没有显著下降。


















