如何在 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 中聚合时间序列数据的主要内容,如果未能解决你的问题,请参考以下文章

MongoDB聚合时间序列

MongoDB、时间序列和聚合框架

如何在mongodb聚合中获取数据作为数组

如何在spring数据mongodb中聚合一个嵌套对象并避免PropertyReferenceException?

如何在 Spring Boot 中使用特定日期范围和聚合从 MongoDB 数据库中检索数据?

如何从 1 分钟的嵌套数组数据中聚合 OHLC 5 分钟(mongodb、mongoose)