MongoDB时间序列数据库设计:小时/分钟/秒与小时/秒小时?

时间:2018-01-13 05:14:49

标签: arrays database mongodb time-series iot

我在academic research project,并使用MongoDB存储加速度计值(IoT /遥测数据)的时间序列数据。粒度是样本,其中采样率可以是1到100Hz之间的任何值。目前我每个文档使用一小时的数据,然后是三维数组,第一级是分钟,第二级是秒,第三级是样本(双数据类型)。这得益于MongoDB的时间序列数据演示(Part 1Part 2)。

e.g。

{
  "_id": "2018011200:4", /* Jan 12, 2018 hour 00 UTC for sensor 4 */
  "z": [
    00: [ /* 00h00m */
      00: [ 0.1, 0.0, -0.1, ... ], /* 00h00m00s */
      01: [ 0.1, 0.0, -0.1, ... ], /* 00h00m01s */
      02: [ 0.1, 0.0, -0.1, ... ], /* 00h00m02s */
      ...
      59: [ 0.1, 0.0, -0.1, ... ]  /* 00h00m59s */
    ], ...
  ]
}

通过这种方式,使用$slice获取数据子集只能在分钟级别完成,例如,如果我想从00:00:00到00:00:01获取数据,我需要从MongoDB获取00:00(包含60秒)的整分钟,然后在应用程序中获取我需要的第二个。此外,如果我想从00:00:59到00:01:01获取数据,那么我需要整整两分钟,然后在每个应用程序子集中将它们合并回来。这有一点IO浪费,在应用程序中也有一些复杂性。顺便说一句,我不需要检索单个样本,最小的检索单位(和存储)是第二个。

我正在考虑一种稍微不同的方法,其中小时文档被直接划分为几秒钟的数组(因为一小时内有3600秒),然后是样本数组。这意味着要获得5秒的数据,我将检索恰好5秒的数组(即使在两个不同的文档中,如果时间范围超过小时)。仍然存在在不同文档中合并两部分秒的应用程序逻辑,但比小时/分钟/秒层次结构简单。

{
  "_id": "2018011200:4", /* Jan 12, 2018 hour 00 UTC for sensor 4 */
  "z": [
    0: [ 0.1, 0.0, -0.1, ... ],   /* 00h00m00s */
    1: [ 0.1, 0.0, -0.1, ... ],   /* 00h00m01s */
    2: [ 0.1, 0.0, -0.1, ... ],   /* 00h00m02s */
    ...
    3599: [ 0.1, 0.0, -0.1, ... ] /* 00h59m59s */
  ]
}

但是,我也担心替代方法存在我不知道的弱点。

你推荐哪一个更好?我需要考虑哪些潜在的陷阱?或许我应该考虑另一种设计?

提前谢谢。

1 个答案:

答案 0 :(得分:1)

我认为你的数据模型过于复杂。

更新文档要比简单地插入文档复杂得多。由于您的粒度似乎是秒,我们完全在BSON datatype UTC datetime提供的粒度范围内:它精确到毫秒。

因此,根据您的数据模型,假设您为每次写入获得单个值,只需使用类似的内容:

{
  _id: new ObjectId(),
  value: 0.1,
  sensor: 4,
  ts: new ISODate()
}

使用此数据模型,我们确保写入尽可能便宜,而不会牺牲信息。然后,您可以使用MongoDB's aggregations在数据中查询有趣的值。一个简单的例子是计算2018-01-01T00:00:00.000Z和2018-01-02T23:59:59.999Z之间传感器4的值数:

db.values.aggregate([
  {"$match":{"sensor":4,"ts":{"$gte":ISODate("2018-01-01"),"$lt":ISODate("2018-01-02")}}},
  {"$sort":{"ts":-1}},
  { "$group": {
      "_id": {
          "year": { "$year": "$ts" },
          "dayOfYear": { "$dayOfYear": "$ts" },
          "hourOfDay": {"$hour":"$ts"},
          "minuteOfHour": {"$minute":"$ts"},
          "secondOfMinute": {
            "$subtract": [ 
              { "$second": "$ts" },
              { "$mod": [{ "$second": "$ts"}, 1] }
            ]
          }
      },
      "count": { $sum: 1 }
    }},
  ],{"allowDiskUse":true})

更好的是,您可以使用$out stage保存您的聚合,以便更快地访问。

编辑:请注意,您必须正确使用索引才能使此方法更有效。就其本身而言,即使使用我相当有限的50M样本文档测试集,聚合也需要几秒钟。通过索引,我们谈论大约80毫秒,给你一个印象。