时间序列1、时间序列介绍2、创建和查询时间序列集合2.1、创建时间序列集合2.2、在时间序列集合中插入测量值2.3、查询时间序列集合2.4、在时间序列集合上运行聚合3、列出数据库中的时间序列集合4、设置时间序列集合的自动删除5、设置时间序列数据的粒度6、向时间序列集合添加从节点索引6.1、创建二级索引6.2、使用二级索引提高排序性能6.3、时间序列集合的最新数据点查询MongoDB数据库支持时间序列数据的存储和管理通过其时间序列集合(Time Series Collections)功能可以有效地存储一段时间内的测量序列。1、时间序列介绍MongoDB数据库的时间序列集合是一种特殊类型的集合设计用于存储时间序列数据。这种数据类型由一系列数据点组成每个数据点都包含一个时间戳和相关的测量值通过分析这些随时间变化的数据点可以获得对数据的深刻理解。时间序列数据通常由以下几个部分组成。时间数据点被记录的时间。元数据一个标签或标记唯一标识一个序列并且很少更改。测量值按时间增量跟踪的数据点通常是随时间变化的键–值对。在MongoDB数据库中使用时间序列集合首先需要创建集合。在创建过程中包括指定时间字段(timeField)和可选的元数据字段(metaField)。时间字段必须具有BSON类型日期而元数据字段用于标记一系列独一无二的文档数据。创建集合后可以插入数据每个数据点都包含时间戳和相应的测量值。MongoDB的时间序列集合功能为存储和分析时间序列数据提供了强大的支持适用于需要追踪随时间变化的数据点的各种应用场景。例如物联网(Internet of ThingsIoT)数据、天气传感器读数、金融市场的价格波动具体见表。总之MongoDB数据库提供的时间序列集合实现了高效率的时间序列数据存储。2、创建和查询时间序列集合在使用时间序列集合时需要注意由于MongoDB数据库版本的兼容性设计人员只能在FeatureCompatibilityVersion设置为5.0或更高版本的系统上创建时间序列集合。2.1、创建时间序列集合MongoDB使用db.createCollection()方法或create命令创建集合具体代码如下db.createCollection(weather,{timeseries:{timeField:timestamp,metaField:metadata}})上面的代码将timeField字段设置为包含时间数据的字段将metaField字段设置为包含元数据的字段。创建时间序列集合后设计人员可以使用collMod方法修改时间序列粒度或存储桶定义。但是设计人员只能增加每个存储桶涵盖的时间跨度却无法减少。可以通过定义一个granularity字段来实现具体代码如下timeseries:{timeField:timestamp,metaField:metadata,granularity:seconds}在MongoDB 6.3及更高版本中设计人员可以定义bucketMaxSpanSeconds和bucketRoundingSeconds字段且这两个字段的值必须相同具体代码如下timeseries:{timeField:timestamp,metaField:metadata,bucketMaxSpanSeconds:300,bucketRoundingSeconds:300}还有一种可选的方式即设置expireAfterSeconds字段使文档在timeField的值达到该值时以及达到该值之后均过期。timeseries:{timeField:timestamp,metaField:metadata,granularity:seconds},expireAgterSeconds:864002.2、在时间序列集合中插入测量值在MongoDB中设计人员插入的每个文档都应包含一个测量值。例如要同时插入多个天气测量值文档可以执行以下命令db.weather.insertMany([{metadata:{sensorId:6688,type:temperature},timestamp:ISODate(2024-08-18T00:00:00.000Z),temp:12},{metadata:{sensorId:6688,type:temperature},timestamp:ISODate(2024-08-18T08:00:00.000Z),temp:11},{metadata:{sensorId:6688,type:temperature},timestamp:ISODate(2024-08-18T16:00:00.000Z),temp:16},{metadata:{sensorId:6688,type:temperature},timestamp:ISODate(2024-08-18T20:00:00.000Z),temp:15},{metadata:{sensorId:6688,type:temperature},timestamp:ISODate(2024-08-19T08:00:00.000Z),temp:11},{metadata:{sensorId:6688,type:temperature},timestamp:ISODate(2024-08-19T16:00:00.000Z),temp:17},{metadata:{sensorId:6688,type:temperature},timestamp:ISODate(2024-08-19T20:00:00.000Z),temp:12},])如果要插入单个文档设计人员可以使用db.weather.insertOne()方法进行操作。2.3、查询时间序列集合在MongoDB中查询时间序列集合的方式与查询标准MongoDB集合的方式相同。例如要从时间序列集合中返回一个文档可以执行以下命令db.weather.findOne({timestamp:ISODate(2024-08-18T00:00:00.000Z)})输出结果如下{timestamp:ISODate(2024-08-18T00:00:00.000Z),metadata:{sensorId:6688,type:temperature},temp:12,_id:ObjectId(6a6f38642b32e82d9d5fc6fb)}2.4、在时间序列集合上运行聚合在MongoDB中设计人员要获得其他更多的查询功能可以使用聚合管道。例如可以执行以下命令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:2024,month:8,day:18}},avgTmp:13.5},{_id:{date:{year:2024,month:8,day:19}},avgTmp:13.333333333333334}]3、列出数据库中的时间序列集合MongoDB数据库可以输出数据库中的集合列表并按各种属性包括集合类型筛选结果设计人员可以使用此功能列出数据库中的所有时间序列集合。首先要列出数据库中的所有时间序列集合请使用带有{type: “timeseries”}过滤器的listCollections命令具体代码如下db.runCommand({listCollections:1,filter:{type:timeseries}})上面的代码会输出对应时间序列的集合具体包括如下选项type: ‘timeseries’。options: { timeseries: { … } }。输出结果如下{cursor:{id:Long(0),ns:test.$cmd.listCollections,firstBatch:[{name:weather,type:timeseries,options:{timeseries:{timeField:timestamp,metaField:metadata,granularity:seconds,bucketMaxSpanSeconds:3600}},info:{readOnly:false}}]},ok:1}4、设置时间序列集合的自动删除MongoDB数据库在创建天气测量值时间序列集合时可以使用expireAfterSeconds参数设置自动删除超过指定秒数的文档具体代码如下db.createCollection(weather,{timeseries:{timeField:timestamp,metaField:metadata,granularity:hours},expireAfterSeconds:86400})在上面的代码中过期阈值为timeField字段值加上指定的秒数。例如考虑天气测量值时间序列集合中的以下文档{metadata:{sensorId:6688,type:temperature}timestamp:ISODate(2024-08-18T00:00:00.000Z),temp:12}上面的文档将于2024-08-18T00:00:00.000Z时间后在数据库中过期一旦存储桶中的所有文档都过期删除过期存储桶的后台任务就会在下一次运行期间删除该存储桶。设计人员如果要在集合中启用自动删除现有时间序列集合的文档可以使用以下collMod命令db.runCommand({collMod:weather,expireAfterSeconds:604801})设计人员如果要更改expireAfterSeconds参数值也可以发出以下相同的collMod命令db.runCommand({collMod:weather,expireAgterSeconds:604801})设计人员如果要检索expireAfterSeconds的当前值可以使用listCollections命令db.runCommand({listCollections:1})结果文档包含时间序列集合的文档其中包含options.expireAfterSeconds字段设计人员如果要禁用自动删除可以使用collMod命令将expireAfterSeconds参数设置为off具体代码如下db.runCommand({collMod:weather,expireAfterSeconds:off})MongoDB数据库不保证过期数据会在过期后立即删除一旦一个存储桶中的所有文档都过期删除过期存储桶的后台任务就会在下一次运行期间删除该存储桶。5、设置时间序列数据的粒度MongoDB数据库创建时间序列集合时会自动创建一个system.buckets系统集合并将输入的时间序列数据分组到存储桶中。通过设置粒度设计人员可以根据数据的引入速率控制对数据进行存储分段的频率。如果要检索当前集合分桶参数可以使用listCollections命令具体代码如下db.runCommand({listCollections:1})对于时间序列集合上面的代码会输出包含granularity、bucketMaxSpanSeconds和bucketRoundingSeconds的参数具体结果如下如果使用给定的granularity参数值一个数据存储桶中包含的最长时间限额为seconds1小时。minutes24小时。hours30天。默认情况下granularity参数设置为seconds。可以将granularity值设置为最接近同一数据源传入测量值之间的时间跨度从而提高性能。例如若要记录来自数千个传感器的天气数据但每5分钟仅记录一次来自每个传感器的数据则可将granularity参数设置为minutes具体代码如下如果将granularity参数设置为hours则会将长达一个月的数据导入事件分组到单个存储桶中从而拖延遍历时间并减慢查询速度。将其设置为seconds会导致每个轮询间隔有多个存储桶其中许多存储桶可能只包含一个文档。设计人员可以使用collMod命令将timeseries.granularity从较短的时间单位增加到较长的时间单位从而更改时间序列粒度。具体代码如下想要更新自定义分桶参数bucketRoundingSeconds和bucketMaxSpanSeconds不是granularity参数​请将这两个自定义参数包含在collMod命令中并将它们设置为相同的值具体代码如下6、向时间序列集合添加从节点索引MongoDB数据库要提高时间序列集合的查询性能可添加一个或多个二级索引以支持常见的时间序列查询模式。6.1、创建二级索引从MongoDB 6.3版本开始MongoDB会自动在metaField和timeField字段上为新集合创建复合索引。设计人员如何创建额外的二级索引呢下面使用具有特定配置的天气数据集合作为示例具体代码如下db.createCollection(weather,{timeseries:{timeField:timestamp,metaField:metadata}})在每个天气数据文档中metadata字段值是一个子文档其中包含天气传感器ID和类型的字段具体输出如下文档集合的默认复合索引会对整个metadata子文档进行索引因此该索引仅用于$eq查询。通过对特定的metadata字段建立索引设计人员可以提高其他查询类型的查询性能。例如$in查询性能就能受益于metadata.type的二级索引具体如下{metadata.type:{$in:[temperature,pressure]}}6.2、使用二级索引提高排序性能MongoDB数据库对时间序列集合的排序操作可以在timeField字段上使用二级索引。在某些条件下排序操作还可以在metaField和timeField字段上使用复合二级索引。聚合管道阶段$match和$sort决定了时间序列集合可以使用哪些索引具体可以用于以下场景{timeField:±1}上的排序使用timeField上的二级索引。对{metaField:±1, timeField: ±1}的排序会在{metaField: ±1, timeField: ±1}上使用默认复合索引。在metaField上具有点谓词时对{timeField:±1}的排序使用{metaField: ±1,timeField: ±1}上的复合二级索引。下面来看一个示例sensorData集合包含来自天气传感器的测量值具体代码如下如果想在timestamp字段上创建二级单字段索引具体代码如下db.sensorData.createIndex({timestamp:1})以下对timestamp字段的排序操作使用二级索引来提高性能具体代码如下想要确认排序操作使用了二级索引请使用.explain(“executionStats”)选项再次运行该操作具体代码如下6.3、时间序列集合的最新数据点查询MongoDB数据库中针对时间序列数据的最新数据点查询会返回给定字段具有最新时间戳的数据点。对于时间序列集合最新数据点查询会获取每个唯一元数据值的最新测量值。例如设计人员可能想要获取所有传感器的最新温度读数可以通过创建以下任意一个索引来提升最新数据点查询的性能。具体代码如下{metadata.sensorId:1,timestamp:1}{metadata.sensorId:1,timestamp:-1}{metadata.sensorId:-1,timestamp:1}{metadata.sensorId:-1,timestamp:-1}下面的命令用于创建一个复合二级索引metaField参数上的索引为升序timeField参数上的索引为降序。db.sensorData.createIndex({metadata.sensorId:1,timestamp:-1})下面的命令展示了最新数据点查询的示例使用了前文中降序timeField参数的复合二级索引。想要确认最新数据点查询是否使用了二级索引可使用.explain(“executionStats”)命令再次运行该操作