MongoDB 时序集合


1 什么是时间序列集合?

时间序列集合可有效地存储一段时间内的测量序列。

时序数据是随时间收集并由一个或多个不变参数唯一标识的任何数据。

标识时序数据的不变参数通常是数据源的元数据。

示例测量元数据
天气数据温度传感器标识符,位置
库存数据股价股票代码,交易所
网站访问者查看次数网址

与普通集合相比,将时序数据存储在时序集合中可提高查询效率,并减少时序数据和二级索引的磁盘使用量。

2 相关使用操作

1)创建时序集合

注意:只能在功能兼容性设置为5.0 的系统上创建时序集合。

在将数据插入时序集合之前,必须使用db.createCollection()方法或 create 命令显式创建集合:

db.createCollection("weather", { timeseries: { timeField: "timestamp" } } )
db.createCollection(
    "weather24h",
    {
       timeseries: {
          timeField: "timestamp",
          metaField: "metadata",
          granularity: "hours"
       },
       expireAfterSeconds: 86400
    }
)

创建时序集合时,请指定以下选项:

属性类型描述
timeseries.timeField字符串必填。包含每个时间序列文档中的日期的字段的名称。时序集合中的文档必须具有有效的 BSON 日期作为 的值。timeField
timeseries.metaField字符串可选。包含每个时序文档中元数据的字段的名称。指定字段中的元数据应为用于标记一系列唯一文档的数据。元数据应该很少(如果有的话)更改。指定字段的名称可能与 不相同。该字段可以是任何类型的字段。_idtimeseries.timeField
timeseries.granularity字符串可选。可能的值为 、 和 。默认情况下,MongoDB 将 设置为高频摄取。”seconds””minutes””hours”granularity”seconds”手动设置参数,通过优化时序集合中数据在内部的存储方式来提高性能。要为 选择一个值,请选择与连续传入测量之间的时间跨度最接近的匹配项。granularitygranularity如果指定 了 ,请考虑字段具有相同唯一值的连续传入测量值之间的时间跨度。如果测量值来自同一源,则通常对字段具有相同的唯一值。timeseries.metaFieldmetaFieldmetaField如果未指定 ,请考虑在集合中插入的所有测量值之间的时间跨度。timeseries.metaField
expireAfterSeconds数字可选。通过指定文档过期后的秒数,启用时序集合中文档的自动删除。MongoDB会自动删除过期的文档。

该选项允许的其他选项包括:timeseries

  • storageEngine
  • indexOptionDefaults
  • collation
  • writeConcern
  • comment

2)将度量值插入到时间序列集合中

您插入的每个文档都应包含一个测量值。

要一次插入多个文档,请使用以下命令:

db.weather.insertMany([{
   "metadata": {"sensorId": 5578, "type": "temperature"},
   "timestamp": ISODate("2021-05-18T00:00:00.000Z"),
   "temp": 12
}, {
   "metadata": {"sensorId": 5578, "type": "temperature"},
   "timestamp": ISODate("2021-05-18T04:00:00.000Z"),
   "temp": 11
}, {
   "metadata": {"sensorId": 5578, "type": "temperature"},
   "timestamp": ISODate("2021-05-18T08:00:00.000Z"),
   "temp": 11
}, {
   "metadata": {"sensorId": 5578, "type": "temperature"},
   "timestamp": ISODate("2021-05-18T12:00:00.000Z"),
   "temp": 12
}, {
   "metadata": {"sensorId": 5578, "type": "temperature"},
   "timestamp": ISODate("2021-05-18T16:00:00.000Z"),
   "temp": 16
}, {
   "metadata": {"sensorId": 5578, "type": "temperature"},
   "timestamp": ISODate("2021-05-18T20:00:00.000Z"),
   "temp": 15
}, {
   "metadata": {"sensorId": 5578, "type": "temperature"},
   "timestamp": ISODate("2021-05-19T00:00:00.000Z"),
   "temp": 13
}, {
   "metadata": {"sensorId": 5578, "type": "temperature"},
   "timestamp": ISODate("2021-05-19T04:00:00.000Z"),
   "temp": 12
}, {
   "metadata": {"sensorId": 5578, "type": "temperature"},
   "timestamp": ISODate("2021-05-19T08:00:00.000Z"),
   "temp": 11
}, {
   "metadata": {"sensorId": 5578, "type": "temperature"},
   "timestamp": ISODate("2021-05-19T12:00:00.000Z"),
   "temp": 12
}, {
   "metadata": {"sensorId": 5578, "type": "temperature"},
   "timestamp": ISODate("2021-05-19T16:00:00.000Z"),
   "temp": 17
}, {
   "metadata": {"sensorId": 5578, "type": "temperature"},
   "timestamp": ISODate("2021-05-19T20:00:00.000Z"),
   "temp": 12
}])

要插入单个文档,请使用db.collection.insertOne()方法。

3)查询时序集合

若要从时序集合中检索一个文档,请输入以下命令:

db.weather.findOne({
   "timestamp": ISODate("2021-05-18T00:00:00.000Z")
})

4)对时序集合运行聚合

有关其他查询功能,请使用聚合框架:

db.weather.aggregate([
   {
      $project: {
         date: {
            $dateToParts: { date: "$timestamp" }
         },
         temp: 1
      }
   },
   {
      $group: {
         _id: {
            date: {
               year: "$date.year",
               month: "$date.month",
               day: "$date.day"
            }
         },
         avgTmp: { $avg: "$temp" }
      }
   }
])

前面的示例按测量日期对所有文档进行分组,然后返回当天所有温度测量值的平均值:

{
  "_id" : {
    "date" : {
      "year" : 2021,
      "month" : 5,
      "day" : 18
    }
  },
  "avgTmp" : 12.714285714285714
}
{
  "_id" : {
    "date" : {
      "year" : 2021,
      "month" : 5,
      "day" : 19
    }
  },
  "avgTmp" : 13
}

5)检查集合是否属于时间序列类型

若要确定集合是否属于时间序列类型,请使用listCollections命令:

db.runCommand( { listCollections: 1.0 } )

如果集合是时序集合,则返回以下内容:

{
    cursor: {
       id: <number>,
       ns: 'test.$cmd.listCollections',
       firstBatch: [
         {
            name: <string>,
            type: 'timeseries',
            options: {
               expireAfterSeconds: <number>,
               timeseries: { ... }
            },
            ...
         },
         ...
       ]
    }
 }

3 行为

时序集合的行为与普通集合类似。您可以像往常一样插入和查询数据。

MongoDB将时间序列集合视为内部集合上的可写非实例化视图,这些集合在插入时自动将时间序列数据组织成优化的存储格式。

查询时序集合时,每个度量值对一个文档进行操作。对时序集合的查询利用优化的内部存储格式并更快地返回结果。

1)指数

时序集合的实现使用内部集合,可减少磁盘使用量并提高查询效率。时序集合按时间自动对数据进行排序和索引。时序集合的内部索引不由列表显示索引。

注:要提高查询性能,可以手动在指定为 和 的字段上添加二级索引:metaFieldtimeField

2)默认压缩算法

时序集合会忽略全局默认压缩算法snappy,而倾向于zstd,除非在创建集合时使用该选项指定了不同的压缩算法。例如,若要将新集合的压缩算法更改为,请添加以下选项:storageEnginesnappyweather

 db.createCollection(
  "weather",
  {
     timeseries: {
        timeField: "timestamp"
     },
     storageEngine: {
        wiredTiger: {
           configString: "block_compressor=snappy"
        }
     }
  }
)

有效选项包括:block_compressor

  • snappy
  • zlib
  • zstd (默认值)
  • none

4 时序集合的操作约束

1)文档大小

文档的上限大小为 4 MB。

2)更新和删除

时序集合仅支持插入操作和读取查询。更新和手动删除操作会导致错误。

要自动删除旧数据,请设置自动删除 (TTL)。

若要从集合中删除所有文档,请使用drop()方法删除集合。

3)二级索引

您可以在指定为 和 的字段上添加二级索引。

如果字段的字段值是文档,则还可以对该文档中的字段创建二级索引:timeFieldmetaFieldmetaField

4)reIndex

时序集合不支持reIndex命令。

5)固定集合

不能将时序集合创建为固定集合。

6)修改集合类型

集合的类型只能在创建集合时设置:

  • 现有集合无法转换为时序集合。
  • 时序集合无法转换为其他集合类型。

若要将数据从现有集合移动到时序集合,请将数据迁移到时序集合中。

7)修改timeFieldmetaField

您只能在创建集合时设置集合和参数。创建后,无法修改这些参数。timeFieldmetaField

8)修改granularity

一旦设置好了,它一次只能增加一个级别。从 到 或 从 到 。不允许进行其他更改。如果需要将 from 更改为 ,请先将 to 增加到 ,然后增加到 。granularity"seconds""minutes""minutes""hours"granularity"seconds""hours"granularity"minutes""hours"

9)架构验证

不能为时序集合指定验证规则。

10)客户端字段级加密

时序集合不支持客户端字段级加密。

11)分片

时序集合当前无法分片。

12)聚合$out和$merge

聚合管道$out阶段,$merge无法输出到时序集合。

13)交易

不能写入事务中的时序集合。

注:事务中支持从时序集合读取。

14)更改流

时序集合不支持更改流。

15)数据库触发器

时序集合不支持数据库触发器。

16)GraphQL API

时间序列集合不支持GraphQL API。

17)地图集搜索

时间序列集合不支持Atlas 搜索。

18)领域同步

时序集合不支持Realm 同步。