阅读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**
问题:
$lte
,$gte
运算符只会在日期范围内变慢,甚至在数字比较时也会变慢吗?$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 }
优化
由于阅读了非索引字段,我觉得有更多时间。
因此docsExamined: 106888
在$project
$group
时,有某种改进
我已将"@h"
数据类型从String
更改为Integer
(NumberInt
),我认为它会进一步改进。
答案 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。