索引失效直接导致慢查询并拖垮MongoDB主库,表现为COLLSCAN、CPU和磁盘IO耗尽、高并发下查询卡死数秒;复合索引不满足最左前缀、字段顺序错误、类型不匹配或$regex前缀不固定均使其失效;低基数字段单独建索引反而加重写压力;必须通过explain验证索引真实生效。
索引失效最直接的后果不是“查得慢一点”,而是大量查询退化为 COLLSCAN,在高并发下迅速耗尽磁盘IO和CPU。你看到的“服务器负载飙升”“Zabbix曲线剧烈抖动”,往往就是几十个未命中索引的查询同时扫全表导致的。尤其当业务存在“写完立刻查”逻辑(如插入订单后立即按状态拉取),主库会频繁锁住,出现 query 操作持续 4–5 秒甚至更久——这不是超时,是真实卡死。
比如你建了 {"orgCode": 1, "fixedStatus": 1, "_id": -1},但实际查询只传了 {"fixedStatus": {"$in": [1, 2]}} 或 {"_id": ObjectId("...")},MongoDB根本不会用这个索引。它不会“挑着用”,只会降级为全表扫描。更隐蔽的是:字段顺序写反({"fixedStatus": 1, "orgCode": 1})、类型不一致(orgCode 是字符串,却传数字 350119)、或用了 $regex 前缀不固定,都会让整个复合索引失效。
像 gender、isDeleted 这类只有 2–3 个值的字段,单独建索引不仅无法加速查询(选择性太低),还会拖慢写入——每次插入/更新都要维护额外B+树节点。Spring Data 的 @Indexed 如果随手加在布尔字段上,上线后写吞吐量可能掉 20%+。真正该建索引的是高频、高选择性的组合,比如 {"tenantId": 1, "createdAt": -1},而不是单个状态位。
别信“我建了索引所以没问题”。必须对线上典型查询跑一遍 db.collection.explain("executionStats").find({...}),盯住三个字段:executionStages.stage 必须是 IXSCAN(不是 COLLSCAN);nReturned 应远小于 totalDocsExamined;executionTimeMillis 要稳定在毫秒级。如果 stage 是 IXSCAN 但 totalKeysExamined 接近总文档数,说明索引覆盖不全,得补投影字段或调整排序逻辑。