Mongo DB 之类的带计数的搜索在 5000 万个集合数据上非常慢

use*_*202 5 mongoose mongodb nosql node.js mongodb-query

在我的应用程序中,我收集了 5000 万条数据。我正在使用类似的搜索,然后计算特定字段(即患者名字)的结果。我还在 Patientfirstname 字段上创建了一个索引,它提高了性能,但仍然需要花费大量时间。

db.患者.find({"Patientfirstname":{"$regex":"Testuser"}}).count() 没有索引40

在 Patientfirstname 字段上添加索引后31秒db. Patients.find({"Patientfirstname":{"$regex":"Testuser"}}).count()

db.patients.find({"Patientfirstname":{"$regex":"Testuser"}}).count()
Run Code Online (Sandbox Code Playgroud)

我尝试了不同的方法(聚合),但响应仍然很慢

 db.patients.aggregate.([{$match:{"Patientfirstname":{"$regex":"Testuser"}}},
{$project:{"Patientfirstname":1,"_id":1}},
{$group : {_id:"$Patientfirstname", count:{$sum:1}}},
{$sort:{"count":-1}} ])
Run Code Online (Sandbox Code Playgroud)

此查询也需要同样的时间来获取结果 31 秒

尝试了另一种方法,但结果不正确

仅从整个集合中选择字段,然后应用搜索、计数和结果等。

db.patients.find({},{Patientfirstname:1,_id:1}).count({"Patientfirstname":{"$regex":"Testuser"}})
Run Code Online (Sandbox Code Playgroud)

在计数中应用过滤器不起作用,显示整个集合计数请帮助此查询更快地获取结果。提前致谢

小智 4

所以这里是交易:

正如评论中正确指出的那样, $regex 是一个无论有索引还是没有索引都不能很好地执行的运算符。原因如下:

没有索引的查询速度很慢,因为它们使用 COLLSCAN 执行 - 这本质上是对磁盘上的整个 5000 万个文档进行逐一迭代,过滤数据并仅返回匹配的文档。磁盘本质上是一种速度较慢的硬件,这也无助于解决这种情况。

现在,当建立索引时,MongoDB 在 RAM 中创建一个 B 树。而且 $regex 运算符本质上不是很有选择性,它会强制在索引 b 树中进行完整的树扫描(与在相等或范围的情况下减少/部分树扫描相比) - 这与集合扫描本身一样糟糕。您在 9 秒内获得优势的唯一原因是因为此树扫描发生在 RAM 而不是磁盘中。

话虽如此,还有一些替代方案:

  1. 优化您的 $regex。来自MongoDB 文档本身:

对于区分大小写的正则表达式查询,如果该字段存在索引,则 MongoDB 将正则表达式与索引中的值进行匹配,这比集合扫描更快。如果正则表达式是“前缀表达式”,则可以进行进一步的优化,这意味着所有潜在的匹配都以相同的字符串开头。这允许 MongoDB 从该前缀构造一个“范围”,并且仅与索引中落在该范围内的那些值进行匹配。

如果正则表达式以插入符号 (^) 或左锚点 (\A) 开头,后跟一串简单符号,则该正则表达式是“前缀表达式”。例如,正则表达式 /^abc.*/ 将通过仅匹配索引中以 abc 开头的值进行优化。

另外,同时/^a/,/^a。/、和/^a。$/ 匹配等效字符串,它们具有不同的性能特征。如果存在适当的索引,则所有这些表达式都使用索引;然而,/^a。/、和/^a。$/ 速度较慢。/^a/ 匹配前缀后可以停止扫描。

不区分大小写的正则表达式查询通常无法有效地使用索引。$regex 实现不支持排序规则,并且无法使用不区分大小写的索引。

  1. 创建文本索引- 这将标记您的文本字符串并启用更快的基于文本的搜索

  2. 如果您部署在 MongoDB Atlas 上 - 那么您可以使用Atlas Search,它是一个基于 Lucene 的文本搜索引擎(工作方式几乎像类固醇上的 elasticsearch)。这提供了显着更高的性能和功能,例如模糊文本搜索、文本自动完成等。