mongodb - 百万数据aggregate group sum 统计超级耗时的问题,求解决方案
PHPz
PHPz 2017-05-02 09:20:16
0
3
1908

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以内能实现吗

PHPz
PHPz

学习是最好的投资!

全部回复(3)
洪涛

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

的索引都没有意义。在找到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就不能期望这么高。如果你真有这方面的需求,就应该从源头入手,考虑:
  1. info每次
    字段有更新或插入时就做好计数

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

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

类似于

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

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

给我你的怀抱

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

热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板