MongoDB发现正则表达式很慢
Posted
技术标签:
【中文标题】MongoDB发现正则表达式很慢【英文标题】:MongoDB find slow with regex 【发布时间】:2013-07-11 00:36:03 【问题描述】:我目前开发的系统,使用的是 MongoDB 2.4.4
我有一个用户集合。
有一个组合索引: "LASTNAME" : 1 , "FIRSTNAME" : 1 , "EMAIL" : 1 , "CITY" : 1 , "STATUS" : 1
我也尝试过使用单个索引,但没有提高性能
系统包含 400.000 条测试记录。
查询(来自 org.springframework.data.mongodb.core.query.Query 的 Java 调试):
"LASTNAME" : "$regex" : "^Schm",
Fields: "FIRSTNAME" : 1 , "EMAIL" : 1 , "CITY" : 1 , "STATUS" : 1 ,"LASTNAME" : 1,
Sort: "LASTNAME" : 1 , "FIRSTNAME" : 1 , "EMAIL" : 1 , "CITY" : 1 , "STATUS" : 1
在 16 毫秒内执行。太棒了。
此查询未显示在 MongoDB 控制台中(此处没有可发布的调试信息)。
但是,我喜欢搜索不仅要以开头,而且应该不区分大小写。
查询:
"LASTNAME" : "$regex" : "^Schm" , "$options" : "i",
Fields: "FIRSTNAME" : 1 , "EMAIL" : 1 , "CITY" : 1 , "STATUS" : 1 , "LASTNAME" : 1,
Sort: "LASTNAME" : 1 , "FIRSTNAME" : 1 , "EMAIL" : 1 , "CITY" : 1 , "STATUS" : 1
在 897 毫秒内执行。这是不可接受的慢。
Mongo 控制台显示如下:
query: query: LASTNAME: /^Schm/i ,
orderby: LASTNAME: 1, FIRSTNAME: 1, EMAIL: 1, CITY:1, STATUS: 1
cursorid:1252405545564528 ntoreturn:25 ntoskip:0 nscanned:297651
keyUpdates:0 numYields: 1 locks(micros) r:1391715 nreturned:25 reslen:4422 897ms
正如我们所见。指向索引问题的不是 scanAndOrder 问题。
然后我尝试以适合大多数情况的下一种方式解决它(从用户插入,小写和大写),但这也更慢。 我的期望是,它执行的时间是第一个查询的三次。
查询:
"$or" : [ "LASTNAME" : "$regex" : "^Schm" , "LASTNAME" : "$regex" : "^schm" , "LASTNAME" : "$regex" : "^SCHM"],
Fields: "FIRSTNAME" : 1 , "EMAIL" : 1 , "CITY" : 1 , "STATUS" : 1 , "LASTNAME" : 1,
Sort: "LASTNAME" : 1 , "FIRSTNAME" : 1 , "EMAIL" : 1 , "CITY" : 1 , "STATUS" : 1
在 1300 毫秒内执行。没什么好说的了。
MongoDB 控制台:
query: query: $or: [ LASTNAME: /^Schm/ , LASTNAME: /^schm/ , LASTNAME: /^SCHM/ ] ,
orderby: LASTNAME: 1, FIRSTNAME: 1, EMAIL: 1, CITY: 1, STATUS: 1
cursorid:43560166842085 ntoreturn:25 ntoskip:0 nscanned:297651
keyUpdates:0 numYields: 1 locks(micros) r:1531168 nreturned:25 reslen:4422 1300ms
那么,我怎样才能搜索不区分大小写,几乎有第一次搜索的速度呢? 最长 150 毫秒!
【问题讨论】:
附带说明,我删除了 Java 标记,因为这个问题只与 Mongo 相关,与 Java 无关 您能否为您的$or
查询发布explain()
我不知道如何,使用 Spring MongoTemplate。目前正在考虑切换到 mongo-java-driver...
你会从 mongo 控制台而不是 Spring 获得 explain()
我现在直接在2.4.5版本的mongo shell上测试了。排序操作让它变得如此缓慢(没有在 16 毫秒内对 or 语句进行排序):-> pastebin.com/Gd0LRPb7
【参考方案1】:
添加不区分大小写后,您就不能再使用索引。在构建需要支持搜索的应用程序时,这是一个重要的设计问题。
要克服这个问题,您应该将已经小写的姓氏版本存储在另一个字段中,并区分大小写进行查询(显然将所有搜索查询转换为小写,然后再将它们传递给 Mongo)。
编辑
似乎在 2.4 中添加了文本搜索。阅读它here,看看它是否能满足您的需求。
附带说明,如果您真的关心性能(从您的问题来看,听起来您可能是这样),您真的应该重新考虑针对您的数据存储引擎进行搜索。考虑使用 ElasticSearch(或简单的 Lucene 索引)等替代搜索引擎,以使搜索流量远离您的主数据存储。
【讨论】:
“一旦添加了不区分大小写的功能,就不能再使用索引”trunc() 时抱怨他们的 SQL 查询在日期上很慢一样。 但是,为什么 or 解决方案这么慢? @Nabor 不管你的$or
解决方案为什么这么慢(我相信这是因为 Mongo 正在回退到索引扫描,因为你给了它 3 个可能的“根”来搜索) ,你应该质疑它是否值得你花时间。您是否真的要构造一个 $or
查询,其中包含查询中每个字母的每个有效大小写排列,作为避免不区分大小写搜索的一种方式?那不会更快了。索引与否。
第一行并不完全正确,一旦您松开前缀锚或用例不敏感,它就无法使用有效使用索引的能力。好点还是提到
@ColinMorelli:完全索引扫描肯定没有有效地使用索引,但通常仍然比完整文档的表扫描要好(如果您需要在未使用的文档中分页,这可能会更糟阅读它们!)。实际上,除非您的数据集非常小(或耐心很高),否则您不应该对这种常见查询方法感到满意。以上是关于MongoDB发现正则表达式很慢的主要内容,如果未能解决你的问题,请参考以下文章