字段类型变更导致索引失效,因MongoDB索引按原始BSON类型严格构建,$set修改类型后新旧值类型不匹配,IXSCAN退化为COLLSCAN;需先删旧索引、更新数据、再建新索引。

字段类型变更后查询变慢,explain()显示IXSCAN消失
直接用$set把字符串改成数字(比如{"status": "1"} → {"status": 1}),旧索引项仍按原始BSON类型编码,新值无法匹配——索引条目和文档实际值类型不一致,查询引擎只能退化为COLLSCAN。
- 改类型前必须先运行
db.collection.getIndexes(),确认该字段是否被索引覆盖 - 已有索引时,不能跳过
dropIndex()直接更新数据;否则重建索引会失败或仍不生效 - 复合索引更敏感:哪怕只改了
{status: 1, createdAt: -1}里的status类型,整个索引都失效
正则表达式查询没走索引,执行计划仍是COLLSCAN
只有前缀匹配能利用索引,比如/^Alice/;而/lice/、/son$/或.*alice这类非前缀正则,MongoDB无法利用索引有序性,等效于全表扫描。
- 检查方式:用
db.collection.explain("executionStats").find({name: /lice/})看executionStages.stage是不是COLLSCAN - 替代方案:对模糊搜索字段单独建文本索引(
createIndex({name: "text"})),但注意它不支持精确匹配优化 - 避免在高频查询字段上依赖通配正则——这不是索引能解决的问题,而是查询设计缺陷
复合索引中范围查询后字段无法命中索引
在索引{a: 1, b: 1, c: 1}上,查询{a: 1, b: {$gt: 2}, c: 3}时,c字段不会走索引——因为b是范围条件,c已超出索引“有效前缀”范围。
- 索引使用遵循“最左前缀原则”,一旦遇到范围操作符(
$gt、$lt、$in等),后续字段全部失效 - 若业务需同时查
b和c,考虑调整索引顺序,如{a: 1, c: 1, b: 1},把等值字段放范围字段前 -
$ne、$not也属于范围语义,同样导致后续字段失效
分片集群里唯一索引不生效,写入重复数据
分片环境下,MongoDB不支持跨分片的单字段唯一索引。如果唯一索引不含完整分片键前缀,插入时不会校验全局唯一性,可能在不同分片上写入相同值。
- 正确做法:唯一索引必须以完整分片键开头,例如分片键是
{orgId: 1, userId: 1},那唯一索引应为{orgId: 1, userId: 1, email: 1} - 检查索引一致性:运行
db.runCommand({checkMetadataConsistency: 1, checkIndexes: true}) - 不要依赖
db.collection.createIndex({email: 1}, {unique: true})来保证分片集合的全局唯一——它只在单个分片内生效

















