mongodb - 百万数据aggregate group sum 统计超级耗时的问题,求解决方案

浏览:48日期:2023-06-25

问题描述

1、文档结构示例

{ _id: xxxx, user: ’xiaoming’, level: 5, from: ’iPhone’, info: ’something wrong’}

2、场景:user为’xiaoming’的文档有六七百万条

3、问题:怎么提升aggregate+group+sum速度

aggregate([ {$match:{user: ’xiaoming’, info:{$regex:’wrong’}}}, {$group:{_id:null, count:{$sum:1}}}])

用上面这个来统计xiaoming带有wrong的文档数量,结果

{'_id': null, 'count': 2299999 }

耗时30s-40s。user、info、user+info三种索引都尝试过,速度都没有提升baidu、google查到‘带条件计数慢无解’怎么提升效率,10s以内能实现吗

问题解答

回答1:

首先要说明的一个问题是,对于OLAP型的操作,期望不应该太高。毕竟是对于大量数据的操作,光从IO就已经远超通常的OLTP操作,所以要求达到OLTP操作的速度和并发是不现实的,也是没有意义的。但并不是说一点优化空间也没有。我们先从索引入手。在没有索引的前提下,找出600万条{user: 'xiaoming'}需要多少时间?全表扫描COLLSCAN从700w条数据中找出600w条,跟从1亿条数据中找出600w条显然是两个概念。命中索引IXSCAN,这个差异就会小很多,几乎可以忽略。所以你说{user: 1}这个索引没有作用是不对的,可能只是因为集合数据量太少看不出差异而已。顺便应该提一下看效率是否有差异应该看执行计划,不要看执行时间,时间是不准确的。在有user索引的前提下,结果仍然有600w条,剩下的部分是个regex,regex无法命中索引,所以不管有没有对info的索引都没有意义。在找到600w条数据之后还有一个对600w数据的filter操作。唯一对这个操作可能有帮助的只有全文索引,但全文索引并不能完全替代正则,具体问题需要读一下文档。考虑全文索引可行的情况下,可以建立复合索引:

db.coll.createIndex({ user: 1, info: 'text'});

对应地查询应该改为:

db.coll.aggregate([ {$match:{user: ’xiaoming’, $text: { $search: 'wrong' }}}, {$group:{_id:null, count:{$sum:1}}}])

关于复合全文索引的介绍参考这里,仍然是有些限制需要注意。这样优化之后预计在同样的硬件下能降到20s以内,跟你要的10s内还有一段距离。原因开头说了,对OLAP就不能期望这么高。如果你真有这方面的需求,就应该从源头入手,考虑:

每次info字段有更新或插入时就做好计数或者

每隔一段时间做一次完整的统计,缓存统计结果,查询的时候直接展现给用户

回答2:

不了解,不过能不能拆分成两个match会不会好一点呢。。

类似于

aggregate([ {$match:{user: ’xiaoming’}}, {$match:{info: /wrong/}}, {$group:{_id:null, count:{$sum:1}}}])

主要我认为正则花时间。有index的话,index一下user。

回答3:

实时性要求不高 可以定时统计 + 缓存

相关文章: