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发现正则表达式很慢的主要内容,如果未能解决你的问题,请参考以下文章

mongodb 正则

MongoDB 正则表达式

mongodb 正则表达式查询性能问题

使用正则表达式在 MongoDB 中搜索任何值?

MongoDB(课时14 正则运算)

使用正则表达式元素数组的 MongoDB 查询 $in