如何在 MongoDB 中聚合时间序列数据
Posted
技术标签:
【中文标题】如何在 MongoDB 中聚合时间序列数据【英文标题】:How to aggregate time series data in MongoDB 【发布时间】:2018-04-16 08:03:39 【问题描述】:我有 MongoDB 文档,就像这里解释的那样 https://www.mongodb.com/blog/post/schema-design-for-time-series-data-in-mongodb
因此,每天(以及类型和系统)有 1 个文档,其中包含包含小时、分钟和秒数据的 values
字段,如下所示:
"_id" : ObjectId("59fc57d75bc7315366b78799"),
"date" : ISODate("2017-11-03T00:00:00.000+0000"),
"system" : "192-168-1-30",
"type" : "memory",
"values" :
[...]
"11" : // hour 11
[...]
"49" : // minute 49
[...]
"43" : NumberInt(62171000), // second 43
"44" : NumberInt(62169000),
[...]
,
"50" :
"1" : NumberInt(62363000),
"2" : NumberInt(62319000)
[...]
,
[...]
,
[...]
,
"updatedAt" : ISODate("2017-11-03T13:34:00.720+0000"),
"createdAt" : ISODate("2017-11-03T11:49:43.442+0000")
例如,在 2017-11-03 的 11:49:43,内存为 62171000。
现在我正在尝试获取这些文档的聚合数据,以获取每分钟、几小时等的平均数据行,但我很困惑如何告诉聚合框架 $values 是一个小时数组,分和秒。
或者我应该使用 map/reduce 吗?
有什么提示吗?
【问题讨论】:
是每个 60秒、1小时等等,还是持续 60秒、1小时等等? 嗯,你是什么意思?我几乎每秒都会获得数据,我希望将其按分钟、小时等平均汇总。 哦,我看到了你所说的部分。 “对于每个”,正在更新... 【参考方案1】:您错过了文章的要点,该文章基本上描述了具有预聚合数据的系统:
“num_samples”和“total_samples”字段会随着新读数应用于文档而更新:
$set: “values.59”: 2000000 ,
$inc: num_samples: 1, total_samples: 2000000
因此每个文档包含每个文档的值的数量和值的总数。因此,如果您将这 2 个字段添加到文档中,您可以通过将 total_values 除以 number_values 轻松计算 每日平均值。
这篇文章很老了。一方面,Mongodb 从那时起显着发展,另一方面提到的项目被放弃了。我特别为 Square Cube 的命运感到遗憾。
从 v3.4 开始,您可以使用objectToArray 来实现运行时分组,如下所示:
db.collection.aggregate([
$project:
date : 1, system : 1, type : 1,
hour: $objectToArray: "$values"
,
$unwind: "$hour",
$project:
date : 1, system : 1, type : 1,
hour: "$hour.k",
minute: $objectToArray: "$hour.v"
,
$unwind: "$minute",
$project:
date : 1, system : 1, type : 1, hour: 1,
minute: "$minute.k",
second: $objectToArray: "$minute.v"
,
$unwind: "$second",
$project:
date : 1, system : 1, type : 1, hour: 1, minute: 1,
second: "$second.k",
value: "$second.v"
,
])
它每秒为您提供以下格式的 1 个文档:
"_id" : ObjectId("59fc57d75bc7315366b78799"),
"date" : ISODate("2017-11-03T00:00:00.000Z"),
"system" : "192-168-1-30",
"type" : "memory",
"hour" : "11",
"minute" : "49",
"second" : "43",
"value" : 62171000
之后您可以应用聚合阶段以按秒、分钟、小时分组。
问题是它非常昂贵,而且小时、分钟和秒都是字符串这一事实只会使其操作更加复杂。
首先每秒存储一个文档会更简单。
【讨论】:
非常感谢,那么您认为拥有大量文档而不是这种(旧)方法更有效率吗?我知道每秒保存 1 个文档可能越来越容易,但我认为我会收集大量记录(我必须监控 10k 个系统)。 @Michelem 你对这个后续问题一针见血。对于数据量不允许在运行时聚合它的系统,预聚合数据是一种利基方法。如果每秒有 1 个文档导致聚合速度太慢,那么答案中的代码将无济于事,因为它的成本更高。您可能希望基于每分钟预先聚合并执行更高级别的聚合运行时。以上是关于如何在 MongoDB 中聚合时间序列数据的主要内容,如果未能解决你的问题,请参考以下文章
如何在spring数据mongodb中聚合一个嵌套对象并避免PropertyReferenceException?