当有数百万条记录时,Mongo计数真的很慢

Chr*_*nch 22 mongodb

//FAST
db.datasources.find().count()
12036788

//SLOW    
db.datasources.find({nid:19882}).count()
10161684
Run Code Online (Sandbox Code Playgroud)

关于nid的索引

有什么办法让第二个查询更快?(需要大约8秒钟)

Rem*_*iet 30

由于MongoDB仍然需要执行完整的b-tree遍历来查找符合条件的适当数量的文档,因此计数查询(索引或其他方式)很慢.其原因是MongoDB b-tree结构未被"计数",这意味着每个节点不存储有关节点/子树中元素数量的信息.

这个问题在这里报告https://jira.mongodb.org/browse/SERVER-1752并且目前没有解决方法来提高性能,除了手动维护该集合的计数器,这显然有一些缺点.

另请注意,db.col.count()版本(因此没有条件)可以占用大的快捷方式,并且实际上不执行查询,因此速度很快.也就是说它并不总是报告与计数查询相同的值,它应该返回所有元素(例如,它不会在具有高写入吞吐量的分片环境中).争论是否是一个错误.我觉得是这样的.

请注意,在2.3+中引入了一个重要的优化,它应该(并确实)提高索引字段计数的性能.请参阅:https://jira.mongodb.org/browse/SERVER-7745

  • 如果计数太慢,我想知道如何使用mongodb进行检查. (2认同)
  • 分页是一个比处理大数据时看起来更复杂的问题。例如,skip(N)是o(N)操作,因此您不能真正基于skip(pageIndex * pageSize).limit(pageSize)进行分页,也不能使用count()确定总页数。您可以,但是随着数据大小的增加,它会变慢,这是一个错误的模式。请注意,有一些解决方案,它们要比这复杂得多。我认为所有上述count()应该比它们快,并且MongoDB应该(至少是可选地)使用计数的b树。 (2认同)
  • 我没有运气搜索没有使用跳过和限制的大数据的分页.如果有人有一个例子,分享会很棒 (2认同)

use*_*439 16

正如@Remon所说,count()必须扫描与查询/过滤器匹配的所有文档.它是O(n),其中n是与索引匹配的文档数,如果字段未编入索引,则为集合中的文档数.

在这种情况下,您通常希望重新审视您的要求.你真的需要一个精确的数字10161684吗?如果精度很重要,则应为特定查询保留单独的计数器.

但在大多数情况下,精确度并不重要.这是两个中的一个:

  • 你不关心它是1000万还是1020万,但数量级是重要的,即你关心它是800万还是1000万.
  • 如果它是一个小的,你只关心精确的数字.也就是说,你有兴趣知道有44个结果或72个.但是一旦它超出了1000个,你就可以说用户找到了"超过1000个对象".

在我的应用程序中,我发现第二个选项是我想要的.因此,我也限制了count()查询,以便计数在达到限制时停止.像这样:

db.datasources.find({nid: 19882}).limit(1000).count(true)
Run Code Online (Sandbox Code Playgroud)

对于用户,如果计数为1000,则显示" 找到1000个或更多结果",否则,我显示确切的数字.

至于第一种选择......我还没有想到一个简洁的解决方案.

  • count()返回匹配的文档数.count()接受一个参数`applySkipLimit`,它会影响计数是在跳过的和有限的集合上,还是整个集合(仅过滤).如果你传递true,它会将计数限制为1000.如果你传递false,它将通过所有匹配的文件来计算 - 这将是缓慢的,而不是你想要的. (3认同)