首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Mongo $sum慢速

Mongo $sum慢速
EN

Stack Overflow用户
提问于 2015-07-20 11:34:04
回答 1查看 1.5K关注 0票数 1

我需要了解一下如何使用芒果来更好地表演。我有两个使用mongo的项目,其中一个项目有1.4亿行,每个查询都在瞬间运行,数据以小块的形式显示,所以使用几个索引,mongo能够过滤99%的数据并快速返回所选的数据。蒙古在这类项目上做得很好。

另一方面,我有另一个项目,如谷歌分析,跟踪访问,点击等。其目的是根据一定的标准(使用表单)在一个时间范围内计算点击量。我在挑战mysql做同样的任务。

初试

我使用了传统的数据模式,逐行进行,如下所示:

代码语言:javascript
复制
{
'user':'abc',
'date':'2015-07-20',
'hour':02,
[....]
'clicks':30
}

使用200+数百万行(即使按小时按小时进行单击),我在每个字段中都有索引,而在查询最多的组中也有一些复合索引。如果结果的行块足够大(更糟的是,总行数),索引会消耗服务器中32 in的内存,那么试图通过某些特定的$sum来聚集和单击$match真的很慢。

二次尝试

利用mongo的模式优势,设计了一个分组模式,使数据尽可能少重复,其中每种类型单击的属性都由字段的唯一组合(具有唯一的索引)确定,然后单击按日期分布的树的分组,行示例:

代码语言:javascript
复制
{
    "user" : "asd",
    [....]
    "date" : {
    "total" : 5,
    "years" : {
        "2015" : {
            "total" : 5,
            "months" : {
                "06" : {
                    "total" : 5,
                    "days" : {
                        "30" : {
                            "total" : 2,
                            "hours" : {
                                "16" : 1,
                                "22" : 1
                            }
                        },
                        "28" : {
                            "total" : 1,
                            "hours" : {
                                "6" : 1
                            }
                        },
                        "29" : {
                            "total" : 2,
                            "hours" : {
                                "14" : 1,
                                "20" : 1
                            }
                        }
                    }
                }
            }
        }
    }
    }
}

感谢这种策略,200+百万行减少了10倍,并且索引在内存中合适,因此插入速度减慢,因为在插入新的“行”之前,必须检查是否找到具有相同特征的行,并将其应用于日期数组的单击合并到以前是否存在的位置。

当我需要计数行时,速度已经与传统模式相比得到了提高,但是我需要做一些像这样的模糊聚合来计数数据:

'$sum'=>'$date.years.'.$year.'.months.'.$month.'.days.'.$day.'.total‘

这在总体上降低了mysql的平均速度,但是差别是如此之大,即使在某些情况下mysql以太多的速度赢得了这场战斗,考虑到mysql有2亿行和2000万个mongo,这是不可接受的,因为mysql在16 s内执行了多次查询,而mongo在120 s内解决了它。我想击败mysql (myIsam)使用mongo作为替代。我尝试了很多东西,从日期树上的稀疏索引到二级缓存,保存一些预处理的结果并混合它们。无法在某一天前缓存所有数据的排列,因为.田里有很多。

碎片可能是一个解决方案,但我认为不会神奇地提高速度2。

请给我一些提示

更新

让我们在几天内搜索某个国家:

Mongo压缩模式

Mongodb计数country = 'AD':11389的行

合计:

代码语言:javascript
复制
Array
(
    [0] => Array
    (
        [$match] => Array
            (
                [country] => AD
            )

    )

    [1] => Array
    (
        [$group] => Array
            (
                [_id] => Array
                    (
                        [country] => $country
                    )

                [2015-07-01] => Array
                    (
                        [$sum] => $date.years.2015.months.07.days.01.total
                    )

                [2015-07-02] => Array
                    (
                        [$sum] => $date.years.2015.months.07.days.02.total
                    )

                [2015-07-03] => Array
                    (
                        [$sum] => $date.years.2015.months.07.days.03.total
                    )

                [2015-07-04] => Array
                    (
                        [$sum] => $date.years.2015.months.07.days.04.total
                    )

                [2015-07-05] => Array
                    (
                        [$sum] => $date.years.2015.months.07.days.05.total
                    )

                [2015-07-06] => Array
                    (
                        [$sum] => $date.years.2015.months.07.days.06.total
                    )

                [2015-07-07] => Array
                    (
                        [$sum] => $date.years.2015.months.07.days.07.total
                    )

                [2015-07-08] => Array
                    (
                        [$sum] => $date.years.2015.months.07.days.08.total
                    )

                [2015-07-09] => Array
                    (
                        [$sum] => $date.years.2015.months.07.days.09.total
                    )

                [2015-07-10] => Array
                    (
                        [$sum] => $date.years.2015.months.07.days.10.total
                    )

                [2015-07-11] => Array
                    (
                        [$sum] => $date.years.2015.months.07.days.11.total
                    )

                [2015-07-12] => Array
                    (
                        [$sum] => $date.years.2015.months.07.days.12.total
                    )

            )

    )

    [2] => Array
    (
        [$project] => Array
            (
                [_id] => $_id
                [dates] => Array
                    (
                        [2015-07-01] => $2015-07-01
                        [2015-07-02] => $2015-07-02
                        [2015-07-03] => $2015-07-03
                        [2015-07-04] => $2015-07-04
                        [2015-07-05] => $2015-07-05
                        [2015-07-06] => $2015-07-06
                        [2015-07-07] => $2015-07-07
                        [2015-07-08] => $2015-07-08
                        [2015-07-09] => $2015-07-09
                        [2015-07-10] => $2015-07-10
                        [2015-07-11] => $2015-07-11
                        [2015-07-12] => $2015-07-12
                    )

            )

    )

)

结果:

代码语言:javascript
复制
Array
(
    [data] => Array
    (
        [AD] => Array
            (
                [_id] => Array
                    (
                        [country] => AD
                    )

                [dates] => Array
                    (
                        [2015-07-01] => 6080
                        [2015-07-02] => 6580
                        [2015-07-03] => 6178
                        [2015-07-04] => 6084
                        [2015-07-05] => 7085
                        [2015-07-06] => 7192
                        [2015-07-07] => 5672
                        [2015-07-08] => 6769
                        [2015-07-09] => 6370
                        [2015-07-10] => 6035
                        [2015-07-11] => 5513
                        [2015-07-12] => 6941
                    )

            )

    )

    [time] => 17.0764780045
)

Mysql贸易模式

Mysql计数行数: 38515

Mysql查询:

代码语言:javascript
复制
SELECT date,sum(clicks) as clicks FROM table WHERE  (  country = "AD" AND  ( date > 20150700 AND date < 20150712  )  ) GROUP BY country,date;

结果:

代码语言:javascript
复制
Array
(
    [0] => Array
    (
        [date] => 20150701
        [clicks] => 6080
    )

    [1] => Array
    (
        [date] => 20150702
        [clicks] => 6580
    )

    [2] => Array
    (
        [date] => 20150703
        [clicks] => 6178
    )

    [3] => Array
    (
        [date] => 20150704
        [clicks] => 6084
    )

    [4] => Array
    (
        [date] => 20150705
        [clicks] => 7085
    )

    [5] => Array
    (
        [date] => 20150706
        [clicks] => 7192
    )

    [6] => Array
    (
        [date] => 20150707
        [clicks] => 5672
    )

    [7] => Array
    (
        [date] => 20150708
        [clicks] => 6769
    )

    [8] => Array
    (
        [date] => 20150709
        [clicks] => 6370
    )

    [9] => Array
    (
        [date] => 20150710
        [clicks] => 6035
    )

    [10] => Array
    (
        [date] => 20150711
        [clicks] => 5513
    )

)
time: 0.25689506530762

Mongodb贸易模式

物品计数:

合计:

代码语言:javascript
复制
Array
(
    [0] => Array
    (
        [$match] => Array
            (
                [country] => AD
                [date] => Array
                    (
                        [$in] => Array
                            (
                                [0] => 20150701
                                [1] => 20150702
                                [2] => 20150703
                                [3] => 20150704
                                [4] => 20150705
                                [5] => 20150706
                                [6] => 20150707
                                [7] => 20150708
                                [8] => 20150709
                                [9] => 20150710
                                [10] => 20150711
                                [11] => 20150712
                            )

                    )

            )

    )

    [1] => Array
    (
        [$group] => Array
            (
                [_id] => Array
                    (
                        [country] => $country
                    )

                [count] => Array
                    (
                        [$sum] => $clicks
                    )

            )

    )

)

结果:

代码语言:javascript
复制
Array
(
    [result] => Array
    (
        [0] => Array
            (
                [_id] => Array
                    (
                        [country] => AD
                    )

                [clicks] => 76499
            )

    )

    [ok] => 1
)
time: 27.8900089264
EN

回答 1

Stack Overflow用户

发布于 2015-07-21 05:29:22

我一直在拖延回答,因为我确信一些MongoDB专家会回答问题。然而,由于没有人给出答案,我将给出几个提示。也许这能帮上忙。但是再说一遍--,我不是MongoDB专家。每样东西都要吃点盐。--

1)你使用的是哪一种版本?如果你还在2.6上--用WiredTiger引擎试试3.0.x (或更新版本)。

2)如果你有大量的数据切分,那会有很大的帮助。这将增加设置的复杂性,但由于您将能够处理部分数据集在paralell,您可以获得显著的速度增长。但是要小心选择正确的切分键。

3)考虑创建几个可以充当较小视图的集合。示例:如果您目前有15个字段在..。很多查询很有可能同时使用1或2。就像国家。再创建一个使用国家数据并跳过rest的集合。如果查询只使用国家字段,而不使用这15个字段中的其他字段,则使用小集合。如果查询使用更多字段,则使用大字段。这样,对国家的查询将更快,因为您将能够更多地分组数据。然而,这并不总是可能的,因为它增加了构建此类小集合的额外复杂性。如果您在某个队列中处理数据(要在大队列中插入),也可以在small中插入。或者,您可以使用一些聚合查询和$out每隔X分钟构建一次较小的表。

4)提出了第三种模式。你的第二个模式很容易放进数据,但很难得到数据。你可以更多地使用数组。这样就很难获取数据,但查询数据要容易得多,速度也要快得多。请记住,在您的第二个模式和我的第三个模式文档示例中,第三个模式文档正在增长,而且可能需要MongoDB在磁盘上移动它们,这是非常慢的操作。测试这是否影响您的设置。潜在集合模式的一个小示例:

代码语言:javascript
复制
{
    "user": "asd",
    [...],
    "date": ISODate("2015-07-01T00:00:00Z"), // first date of the month
    "total": 2222,
    "daily": [
        {"date": ISODate("2015-07-01T00:00:00Z"), "total": 22},
        {"date": ISODate("2015-07-11T00:00:00Z"), "total": 200},
        {"date": ISODate("2015-07-20T00:00:00Z"), "total": 2000},
   ]
}

在插入数据时,可以使用$criteria = ["user": "asd", "daily.date": new MongoDate("...."), // other fields]和update子句$update = ['$inc': ["total: 1, 'daily.$.total': 1]] (如果您在PHP中)。检查更新了多少行。如果为0,则从相同的数据创建insert。即取消设置$criteria['daily.date']并将更新更改为$update = ['$inc' => ['total' => 1], '$push' => ['daily' => ['date' => new MonoDate('..'), 'total': 1]]]。请记住,如果您有几个插入数据的脚本,您可能会遇到问题。最好把每件事一字一句地排好。或者并行执行,确保$push不会导致添加几个具有相同日期的daily.date。所以-你试图更新,如果不能更新,插入。由于您使用数组和可能性运算符,所以不能使用上部服务器。这就是为什么需要额外的插入。正如我所说的,获取数据要复杂得多。但这将更容易获得数据。确保建立适当的索引。例如,在“daily.date”等方面,这样更新查询就不需要检查大量的文档。甚至-你可以创建一些哈希字段.能保存散列的字段..。字段。并在更新中使用。这样,创建小索引就更容易找到特定的文档(输入索引'daily.date‘、散列字段等等,但不需要放15。)。当您有这样的结构时,您可以使用查询做很多事情。例如-如果你需要整整几个月,只需查询日期和.你需要的字段,总和,你是好的。如果您需要一些日期范围(比如一个月的第一至第十天),您可以按.字段和日期,项目消除不必要的字段,$unwind每日,再次匹配,但这次在daily.date字段,然后项目重命名字段,然后分组和和。它比使用$date.years.2015.months.07.days.03.total灵活得多。

记住,所有这些都只是暗示。自己测试每件事。也许有1 0 5个提示就行了。但这会让一切都变得不同。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/31515447

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档