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以内能实现吗
首先要说明的一个问题是,对于OLAP型的操作,期望不应该太高。毕竟是对于大量数据的操作,光从IO就已经远超通常的OLTP操作,所以要求达到OLTP操作的速度和并发是不现实的,也是没有意义的。但并不是说一点优化空间也没有。
我们先从索引入手。在没有索引的前提下,找出600万条
{user: "xiaoming"}
需要多少时间?全表扫描COLLSCAN
从700w条数据中找出600w条,跟从1亿条数据中找出600w条显然是两个概念。命中索引IXSCAN
,这个差异就会小很多,几乎可以忽略。所以你说{user: 1}
这个索引没有作用是不对的,可能只是因为集合数据量太少看不出差异而已。顺便应该提一下看效率是否有差异应该看执行计划,不要看执行时间,时间是不准确的。在有
user
索引的前提下,结果仍然有600w条,剩下的部分是个regex
,regex
无法命中索引,所以不管有没有对info
的索引都没有意义。在找到600w条数据之后还有一个对600w数据的filter
操作。唯一对这个操作可能有帮助的只有全文索引
,但全文索引并不能完全替代正则,具体问题需要读一下文档。考虑全文索引可行的情况下,可以建立复合索引:对应地查询应该改为:
关于复合全文索引的介绍参考这里,仍然是有些限制需要注意。这样优化之后预计在同样的硬件下能降到20s以内,跟你要的10s内还有一段距离。原因开头说了,对OLAP就不能期望这么高。如果你真有这方面的需求,就应该从源头入手,考虑:
每次
info
字段有更新或插入时就做好计数或者
每隔一段时间做一次完整的统计,缓存统计结果,查询的时候直接展现给用户
不了解,不过能不能拆分成两个match会不会好一点呢。。
类似于
主要我认为正则花时间。
有index的话,index一下user。
实时性要求不高 可以定时统计 + 缓存