范围支持随时间推移分析

时间:2017-01-22 08:33:00

标签: mongodb mongodb-query aggregation-framework analytics

阅读this,然后尝试进行试验。

以下是我的架构。

SCHEMA:

{
    "s"  : "CB",
    "c"  : "REQ_RCV",
    "e"  : "sms_click",
    "st" : "i",
    "b"  : "2",
    "a"  : "1",
    "u"  : "b1_h1_d1_m1_user_2",
    "c#" : "b1_h1_d1_m1_cr-2",
    "@"  : ISODate("2016-10-01T06:03:00.000Z"), //Indexed
    "@h" : "16100106", //hourly bucket
    "@d" : "161001",                            //Indexed
    "@m" : "1610"
}

以下是解释计划:

> 2017-01-22T13:43:47.764+0530 I COMMAND  [conn34] command test-analytics.template3 appName: "MongoDB Shell" command: aggregate {
> aggregate: "template3", pipeline: [ { $match: { @: { $gte: new
> Date(1483228800000), $lte: new Date(1483315199000) } } }, { $group: {
> _id: { b: "$b", HOURLY: "$@h", s: "$s" }, count: { $sum: 1.0 } } }, { $project: { _id: 0.0, BUCKET: "$_id.b", SERVICE: "$_id.s", TIME:
> "$_id.HOURLY", count: 1.0 } }, { $sort: { SERVICE: 1.0, BUCKET: 1.0,
> TIME: 1.0 } } ], cursor: {} } planSummary: IXSCAN { @: 1.0 }
> keysExamined:106888 docsExamined:106888 hasSortStage:1
> cursorExhausted:1 numYields:925 nreturned:96 reslen:7095 locks:{
> Global: { acquireCount: { r: 1860 } }, Database: { acquireCount: { r:
> 930 } }, Collection: { acquireCount: { r: 929 } } }
> protocol:op_command **3499ms**


> 2017-01-22T13:44:24.825+0530 I COMMAND  [conn34] command test-analytics.template3 appName: "MongoDB Shell" command: aggregate {
> aggregate: "template3", pipeline: [ { $match: { @d: "170101" } }, {
> $group: { _id: { b: "$b", HOURLY: "$@h", s: "$s" }, count: { $sum: 1.0
> } } }, { $project: { _id: 0.0, BUCKET: "$_id.b", SERVICE: "$_id.s",
> TIME: "$_id.HOURLY", count: 1.0 } }, { $sort: { SERVICE: 1.0, BUCKET:
> 1.0, TIME: 1.0 } } ], cursor: {} } planSummary: IXSCAN { @d: 1.0 } keysExamined:106888 docsExamined:106888 hasSortStage:1
> cursorExhausted:1 numYields:865 nreturned:96 reslen:7095 locks:{
> Global: { acquireCount: { r: 1740 } }, Database: { acquireCount: { r:
> 870 } }, Collection: { acquireCount: { r: 869 } } }
> protocol:op_command **1294ms**

问题

  1. 虽然两个查询都检查了相同数量的文档 为什么输出会有时差?
  2. $lte$gte运算符只会在日期范围内变慢,甚至在数字比较时也会变慢吗?
  3. 由于分段提供更快的响应,如何使用分段进行范围查询?我可以进行多次聚合时间桶调用以支持范围查询,但这会产生更多的往返时间,任何建议吗?
  4. 是否可以在聚合查询中使用$and两个$match来支持范围分组?目前聚合接受多个$match,但$match的第一个输出会被提供给第二个$match,但我想要的是将单个$match结果添加/分组到下一个管道。< / LI>

    第四季的可能答案:

    db.template3.aggregate([ 
        {
            $match: {
                $or: [
                    {"@d":"170301"},
                    {"@d":"170306"}, 
                    {"@d":"170202"}, 
                    {"@d":"170303"},
                    {"@d":"170304"}, 
                    {"@d":"170305"}
                ]
            }
        },
        { $project: { _id: 0, "b": 1, "s": 1, "@h": 1 } }, 
        {
            $group: {
                _id: {"b": "$b", "HOURLY": "$@h", "s": "$s" }, 
                count: { $sum: 1 } 
            }
        },  
        { 
            $project: {
                _id: 0, 
                "BUCKET": "$_id.b",
                "SERVICE": "$_id.s",
                "TIME": "$_id.HOURLY", 
                count: 1
            }
        },         
        { $sort: { "SERVICE": 1, "BUCKET": 1, "TIME": 1 } } 
    ]);
    

    在这个答案中,我们可以使用桶的混合(每日和每月),但仍然会使用自己的索引。阅读https://docs.mongodb.com/manual/reference/operator/query/or/#or-clauses-and-indexes

    示例查询:

    db.template3.aggregate([ 
    
        {$match:{"@h":{$gte : 17020511, $lte : 17030511}, "st":"i"}}, 
    
        {$project : {"_id":0,  "@h":1,"c":1, "@m":1}}, 
    
        {$group:{_id:{ "HOURLY":"$@h", "c":"$c"}, count:{$sum:1}}},
    
        {$project : {_id:0, "COUNTER":"$_id.c","TIME":"$_id.HOURLY", count:1}}, 
    
        {$sort:{"COUNTER":1,"TIME":1}}
    ]);
    

    输出

    { "count" : 2255, "COUNTER" : "REQ_RCVD", "TIME" : 17020511 }
    { "count" : 28888, "COUNTER" : "REQ_RCVD", "TIME" : 17020600 }
    { "count" : 37613, "COUNTER" : "REQ_RCVD", "TIME" : 17020601 }
    { "count" : 6723, "COUNTER" : "REQ_RCVD", "TIME" : 17020602 }
    { "count" : 14057, "COUNTER" : "REQ_RCVD", "TIME" : 17020603 }
    { "count" : 12405, "COUNTER" : "REQ_RCVD", "TIME" : 17020604 }
    { "count" : 2392, "COUNTER" : "REQ_RCVD", "TIME" : 17020611 }
    { "count" : 28784, "COUNTER" : "REQ_RCVD", "TIME" : 17020700 }
    { "count" : 37494, "COUNTER" : "REQ_RCVD", "TIME" : 17020701 }
    { "count" : 6697, "COUNTER" : "REQ_RCVD", "TIME" : 17020702 }
    { "count" : 13930, "COUNTER" : "REQ_RCVD", "TIME" : 17020703 }
    { "count" : 12493, "COUNTER" : "REQ_RCVD", "TIME" : 17020704 }
    { "count" : 2225, "COUNTER" : "REQ_RCVD", "TIME" : 17020711 }
    { "count" : 28821, "COUNTER" : "REQ_RCVD", "TIME" : 17020800 }
    { "count" : 37949, "COUNTER" : "REQ_RCVD", "TIME" : 17020801 }
    { "count" : 6676, "COUNTER" : "REQ_RCVD", "TIME" : 17020802 }
    { "count" : 14039, "COUNTER" : "REQ_RCVD", "TIME" : 17020803 }
    { "count" : 12349, "COUNTER" : "REQ_RCVD", "TIME" : 17020804 }
    { "count" : 2332, "COUNTER" : "REQ_RCVD", "TIME" : 17020811 }
    { "count" : 28379, "COUNTER" : "REQ_RCVD", "TIME" : 17020900 }
    

    优化

    1. 由于阅读了非索引字段,我觉得有更多时间。 因此docsExamined: 106888

    2. $project

    3. 之前使用$group时,有某种改进
    4. 我已将"@h"数据类型从String更改为IntegerNumberInt),我认为它会进一步改进。

1 个答案:

答案 0 :(得分:1)

让我们一个接一个地提出你的问题:

  

虽然两个查询都检查了相同数量的文件但为什么输出有时间差异?

仅从一次执行中查看性能指标实际上并非如何工作。在结束之前你应该考虑几次执行的平均值,因为有几个因素在起作用。 话虽这么说,MongoDB会将最常用的文档缓存在内存中,并将其保留在那里,除非它必须为其他文档产生内存。因此,如果查询访问已从先前查询缓存的文档,则它应该更快。

同样在MongoDB中,聚合仅在开头使用索引(如果有的话)。例如,$match$sort阶段可以使用索引。在你的情况下,$match是第一个管道阶段,所以这是一个胜利。

  

是$ lte,$ gte只在日期范围内很慢,或者在数字比较上也很慢..?

在MongoDB中,数据存储在BSON中,因此在比较时会dates are basically numbers。所以没有区别。

  

由于bucketing提供更快的响应,如何使用bucketing进行范围查询?我可以进行多次聚合时间桶调用以支持范围查询,但这会产生更多的往返时间,任何建议吗?

虽然我没有测试过,但我确实怀疑time_bucket方法会给出更快的响应。由于created_at将始终增加,因此在这种情况下,索引也将在没有time_bucket的情况下附加到末尾。此外,在数组上创建索引大小时比在简单日期字段上创建的索引大小要大。这不会导致在RAM中拟合索引的问题。

在匹配之前在日期字段上使用某个函数时,使用time_bucket是有意义的。如果在匹配之前仅从日期字段中提取年份,则会使日期上的现有索引无效。

最好将参数转换为与数据库中的数据类型匹配,而不是相反。

  

聚合查询中可能$and两个$match是否支持范围分组。目前Aggregate接受多个$match,但$ match的第一个输出被赋予第二个$ match,但我想要的是将单个$match结果添加/分组到下一个管道。

是的,这是可能的。如果是$and,则只需在$match阶段指定用逗号分隔的所有过滤器。如果是$or,请使用$or运算符。

如果你有two $macth phases one by one MongoDB combines it to one。因此,您无需担心添加多个匹配阶段的结果。

现在您的优化积分

  

由于阅读了非索引字段,我觉得有更多时间。因此docsExamined:106888

是的,covered queries要快得多。

  

在$ group

之前使用$ project时有一些改进

如果使用$group$project阶段减少了文档的大小,那么是的,这是真的。

  

我已将@h dataType从string更改为int(NumberInt),我认为它会进一步改进。

这不一定是真的,但通常情况就是如此。您可以查看this answer