MongoDB $or结合正则查询未使用索引,查询极慢求助
兄弟,我太懂这种查询卡到怀疑人生的感觉了!你遇到的核心问题其实和索引类型、正则写法直接相关,咱们一步步拆解:
为什么你的索引没生效?
MongoDB的B树索引只支持前缀匹配的正则,也就是^app这种以指定字符串开头的正则。而你用的.*app.*是"包含匹配",这种写法会让MongoDB直接放弃索引,全表扫描30万条数据,慢是必然的。不管是单键索引还是{description:1, username:1}的复合索引,都救不了这种正则写法。
具体解决思路
1. 优先改成前缀匹配(最快方案)
如果业务场景允许(比如你要找的是app开头的描述或用户名),把正则改成^app.*,这样索引就能直接生效:
const query = { $or: [ {description: { $regex: "^app.*", $options: "i" }}, // 加i是忽略大小写,不需要可以去掉 {username: { $regex: "^app.*", $options: "i" }} ] };
改完后用db.collection.find(query).explain("executionStats")看执行计划,肯定会走你创建的单键或复合索引,速度能秒级响应。
2. 用文本索引处理包含匹配(必选包含场景)
如果你必须做"包含app字符串"的搜索,别用正则了,直接给username和description创建文本索引:
db.collection.createIndex({ description: "text", username: "text" })
然后用文本查询替代正则+$or,写法更简洁,性能提升N倍:
db.collection.find({ $text: { $search: "app" } })
文本索引专门针对全文搜索优化,30万条数据的话,查询耗时应该能降到几百毫秒以内。
3. 排查索引状态
先确认你的索引真的创建完成了,执行db.collection.getIndexes()看看列表里有没有你创建的索引。如果是刚创建的复合索引,30万条数据可能需要一点时间构建,等构建完成再测试。
4. 避开$or的索引陷阱
如果坚持用正则,要注意$or的每个分支都需要有对应的单键索引——也就是description和username各自的单键索引,而不是复合索引。MongoDB的$or会对每个分支单独查询再合并结果,复合索引在这里帮不上忙。不过还是那句话,只要正则是.*开头,单键索引也没用,所以优先考虑前两个方案。
最后再提醒下,用explain("executionStats")是排查索引问题的神器,看executionStats.totalDocsExamined是不是等于你的集合总数,如果是,说明还是全表扫描,得调整查询或索引。
内容的提问来源于stack exchange,提问作者Make-HCI

