MongoDB聚合性能

Yar*_*ler 19 mongodb mongodb-query

我们遇到聚合查询运行很长时间(几分钟)的问题.

采集:

我们收集了2.5亿份文档,每个文档大约有20个字段,集合的总大小为110GB.

我们有"our_id"和dtKey字段的索引.

硬件:

记忆:

24GB RAM(6*4GB DIMMS 1333 Mhz)

磁盘:

Lvm 11TB由4个3TB磁盘盘构成:

  • 600MB/s的最大瞬时数据传输.

  • 7200 RPM主轴.平均延迟= 4.16ms

  • RAID 0

中央处理器:

2*E5-2420 0 @ 1.90GHz共12个内核,24个线程.戴尔R420.

问题:我们正在尝试进行以下聚合查询:

db.our_collection.aggregate(
    [
        {
            "$match":
            {
                "$and":
                    [
                        {"dtKey":{"$gte":20140916}},
                        {"dtKey":{"$lt":20141217}},
                        {"our_id":"111111111"}
                    ]
            }
        },
        {
            "$project":
            {
                "field1":1,
                "date":1
            }
        },
        {
            "$group":
            {
                "_id":
                {
                    "day":{"$dayOfYear":"$date"},
                    "year":{"$year":"$date"}
                },
                "field1":{"$sum":"$field1"}
            }
        }
    ]
);
Run Code Online (Sandbox Code Playgroud)

此查询需要几分钟才能运行,当它运行时我们可以看到以下内容:

  • Mongo目前的运营收益超过300K次
  • 在iostat上,我们看到~100%的磁盘利用率

完成此查询后,它似乎在缓存中,这可以在瞬间完成,

在为3-4个用户运行它之后,似乎第一个已经从缓存中换出,并且查询需要很长时间.

我们测试了匹配部分的计数,看到我们有50K文档的用户以及500K文档的用户,

我们试图只获得匹配的部分:

db.pub_stats.aggregate(
    [
        {
            "$match":
            {
                "$and":
                    [
                        {"dtKey":{"$gte":20140916}},
                        {"dtKey":{"$lt":20141217}},
                        {" our_id ":"112162107"}
                    ]
            }
        }
    ]
);
Run Code Online (Sandbox Code Playgroud)

查询似乎需要大约300-500M的内存,

但在运行完整查询后,似乎需要3.5G的内存.

问题:

  1. 为什么聚合的流水线操作占用了这么多内存?
  2. 我们如何才能提高它在HTTP请求的合理时间内运行的性能?

mne*_*syn 21

  1. 为什么聚合的流水线操作占用了这么多内存?

只需执行一个$match就不必读取实际数据,就可以在索引上完成.通过投影的访问field1,必须读取实际文档,并且它也可能被缓存.

此外,分组可能很昂贵.通常,如果您的分组阶段需要超过100M的内存,它应报告错误 - 您使用的是哪个版本?它需要在产生之前扫描整个结果集,并且MongoDB必须至少存储组中每个元素的指针或索引.我猜内存增加的关键原因是前者.

  1. 我们如何才能提高它在HTTP请求的合理时间内运行的性能?

dtKey似乎编码时间,并且分组也是基于时间完成的.我试图利用这个事实 - 例如,通过预先计算每天和our_id组合的聚合- 如果没有更多的标准并且数据不再发生太大变化,那就很有意义了.

否则,我会尝试将{"our_id":"111111111"}标准移动到第一个位置,因为相等应始终位于范围查询之前.我想聚合框架的查询优化器足够聪明,但值得一试.此外,您可能希望尝试将两个索引转换为单个复合索引{ our_id, dtkey }.现在支持索引交叉点,但我不确定它的效率如何.使用内置配置文件并.explain()分析您的查询.

最后,MongoDB设计用于大量使用,并且在几毫秒内从磁盘扫描数百GB的数据集在计算上根本不可行.如果您的数据集大于RAM,则由于所有必需的磁盘操作,您将面临数十毫秒及以上,数十或数十万次的大规模IO延迟.请记住,随机访问您将永远不会接近理论顺序磁盘传输速率.如果你不能预先计算,我想你需要更多的RAM.也许SSD会有所帮助,但这只是猜测.

  • 是的,顺序很重要,但它应该是`{our_id:1,dtKey:1}`,因为`our_id`是一个相等条件,而在一个范围查询中是`dtKey`.查询中的顺序应该相应地改变,这就是我在答案中所写的内容,但也许并不是很好. (7认同)
  • 深入挖掘我的大脑:你是对的.+1*2 (2认同)