时序集合(5.0)
时间序列集合有效地存储一段时间内的测量序列。与普通集合相比,在时间序列集合中存储时间序列数据可以提高查询效率,并减少时间序列数据和二级索引的磁盘使用。
1,创建集合
只能在featureCompatibilityVersion设置为5.0的系统上创建时间序列集合。
db.adminCommand( { setFeatureCompatibilityVersion: "5.0" } );
# 创建2个时序集合
db.createCollection("weather", { timeseries: { timeField: "timestamp" } } );
db.createCollection(
"weather24h",
{
timeseries: {
timeField: "timestamp",
metaField: "metadata",
granularity: "hours"
},
expireAfterSeconds: 86400 # 删除超过指定秒数的文档
}
);
Field | Type | Description |
---|
timeseries.timeField | string | 必需的。包含每个时间序列文档中的日期的字段的名称。时间序列集合中的文档必须有一个有效的BSON日期作为时间字段的值。 |
timeseries.metaField | string | 可选的。在每个时间序列文档中包含元数据的字段的名称。指定字段中的元数据应该是用于标记唯一文档的数据。元数据应该很少改变(如果有的话)。指定字段的名称不能是_id或与timeseries.timeField相同。该字段可以是除数组以外的任何类型。 |
timeseries.granularity | string | 可选的。取值为“seconds”、“minutes”和“hours”。在默认情况下,MongoDB为高频写入设置粒度为“秒”。手动设置粒度参数,通过优化时间序列集合中的数据在内部的存储方式来提高性能。要为粒度选择一个值,请选择与连续传入度量之间的时间跨度最接近的匹配。如果指定时间序列。考虑连续传入度量之间的时间跨度,这些度量对于metaField字段有相同的唯一值。如果度量值来自相同的来源,那么metaField字段的度量值通常是相同的。如果不指定时间序列。metaField,考虑所有插入到集合中的度量之间的时间跨度。 |
expireAfterSeconds | number | 可选的。通过指定文档过期后的秒数,启用时间序列集合中的文档的自动删除。MongoDB自动删除过期文档。 |
其他允许的时序集合选项:
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
}]);
3,操作
1,查询一个文档
db.weather.findOne({
"timestamp": ISODate("2021-05-19T16:00:00.000Z")
});
# 输出
{
"timestamp" : ISODate("2021-05-19T16:00:00Z"),
"_id" : ObjectId("60f53f5aa23d5757b8242157"),
"temp" : 17,
"metadata" : [
{
"sensorId" : 5578
},
{
"type" : "temperature"
}
]
}
2,聚合操作
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" : 19 } }, "avgTmp" : 12.833333333333334 }
{ "_id" : { "date" : { "year" : 2021, "month" : 5, "day" : 18 } }, "avgTmp" : 12.833333333333334 }
4,检查是否是时序集合
db.runCommand( { listCollections: 1.0 } );
# 输出中含有 "type" : "timeseries"
5,时序集合的局限性
约束
文档最大大小为4MB
更新和删除
时间序列集合只支持插入操作和读取查询。更新和手动删除操作会导致错误。
要自动删除旧数据,请设置自动删除(TTL)。
若要从集合中删除所有文档,请使用drop()方法删除集合。
二级索引
可以在指定的字段上添加二级索引,如timeField和metaField。如果metaField字段的值是一个文档,还可以在该文档中的字段上创建二级索引。
metafield不支持以下索引类型:
2d
2dsphere
text
二级索引不支持以下索引属性:
TTL
Unique
Partial
重建索引
时间序列集合不支持reIndex命令。
有上限的集合
不能将时间序列集合创建为有上限的集合
修改集合类型
只能在创建集合时设置集合的类型:
现有集合不能转换为时间序列集合。
时间序列集合不能转换为不同的集合类型。
修改timeField和metaField
只能在创建集合时设置集合的timeField和metaField参数。创建后,这些参数不能被修改。
修改granularity
一旦设置了granularity,每次只能增加一个级别。从“秒”到“分钟”,或者从“分钟”到“小时”。其他更改是不允许的。如果需要将粒度从“秒”改为“小时”,请先将粒度增加到“分钟”,再增加到“小时”。
Schema Validation
不能为时间序列集合指定验证规则
客户端字段级加密
时间序列集合不支持客户端字段级别加密
分片
时间序列集合目前不能被分片
Aggregation $out and $merge¶
聚合管道阶段$out和$merge不能输出到时间序列集合
Transactions
不能在事务中写入时间序列集合
事务中支持从时间序列集合读取数据。
Change Streams
时间序列集合不支持
Database Triggers
时序集合不支持
GraphQL API
时序集合不支持
Atlas Search
时序集合不支持
Realm Sync
时序集合不支持
6,时序集合自动删除文档设置
# 过期阈值是timeField字段值加上指定的秒数
# 启用集合上的自动删除
# 要对现有时间序列集合的文档的自动删除,执行以下命令:
db.runCommand({
collMod: "weather24h",
expireAfterSeconds: 604801
});
# 修改expireAfterSeconds参数值,执行以下命令:
db.runCommand({
collMod: "weather24h",
expireAfterSeconds: 604801
});
# 获取expireAfterSeconds当前值,执行以下命令:
db.runCommand( { listCollections: 1 } );
# 禁用自动删除文档,执行以下命令:
db.runCommand({
collMod: "weather24h",
expireAfterSeconds: "off"
});
时序集合删除操作:
MongoDB不保证过期的数据会在过期后立即被删除。一旦桶中的所有文档都过期了,删除过期桶的后台任务将在下一次运行时删除桶。单个桶允许覆盖的最大时间跨度由时序集合的的granularity控制:
granularity Covered Time Span
"seconds" (default) one hour
"minutes" 24 hours
"hours" 30 days
删除过期桶的后台任务每60秒运行一次。因此,文档可能会在文档到期、桶中所有其他文档到期和后台任务运行之间的一段时间内保留在集合中
由于删除操作的持续时间取决于mongod实例的工作负载,过期数据可能在后台任务运行间隔60秒之外还存在一段时间
7,设置时序数据的Granularity
*必须运行MongoDB 5.0.1或更高版本,以便在创建时序集合后更改Granularity
准确设置Granularity参数可以提高性能,通过优化数据在内部的存储方式。
要准确地设置参数,请选择一个Granularity值,该值与metaField字段的值所指定的唯一数据源的摄取速率最接近。
例如,如果您的metaField数据标识天气传感器,并且您每5分钟从每个传感器摄取一次数据,那么您应该选择“分钟”。
获取时序集合的granularity 值
db.runCommand( { listCollections: 1 } );
修改时序集合的granularity 值
db.runCommand({
collMod: "weather24h",
timeseries: { granularity: "hours" }
});
* 一旦设置了granularity,每次只能增加一个级别。从“秒”到“分钟”,或者从“分钟”到“小时”。其他更改是不允许的。如果需要将粒度从“秒”改为“小时”,请先将粒度增加到“分钟”,再增加到“小时”。
8,在metaField and timeField添加二级索引
为了提高时间序列集合的查询性能,可以添加一个或多个二级索引来支持常见的时间序列查询模式。具体地说,在指定为timeField和metaField的字段上创建一个或多个复合索引。如果metaField字段的字段值是一个文档,则可以在该文档中的字段上创建二级索引。
在metadata.sensorId和timestamp字段上,创建符合索引:
db.weather24h.createIndex({ "metadata.sensorId": 1, "timestamp": 1 });
查询集合的索引名字:
db.weather24h.getIndexes();
[
{
"v" : 2,
"key" : {
"metadata.sensorId" : 1,
"timestamp" : 1
},
"name" : "metadata.sensorId_1_timestamp_1"
}
]
查询使用指定的索引:
db.weather24h.find({ "metadata.sensorId": 5578 }).hint("metadata.sensorId_1_timestamp_1");
9,迁移数据到时序集合
9.1,创建一个新的时序集合
db.createCollection(
"weathernew", {
timeseries: {
timeField: "ts",
metaField: "metaData",
granularity: "hours"
}
}
);
9.2,数据变换
时间序列集合支持metaField的字段上的二级索引。如果时间序列数据的数据模型没有指定用于元数据的字段,可以转换数据以创建一个字段。要转换现有集合中的数据,请使用$merge或$out创建一个带有时间序列数据的临时集合。
创建以下格式的天气数据集合
db.weather_data.insertOne(
{
"_id" : ObjectId("5553a998e4b02cf7151190b8"),
"st" : "x+47600-047900",
"ts" : ISODate("1984-03-05T13:00:00Z"),
"position" : {
"type" : "Point",
"coordinates" : [ -47.9, 47.6 ]
},
"elevation" : 9999,
"callLetters" : "VCSZ",
"qualityControlProcess" : "V020",
"dataSource" : "4",
"type" : "FM-13",
"airTemperature" : { "value" : -3.1, "quality" : "1" },
"dewPoint" : { "value" : 999.9, "quality" : "9" },
"pressure" : { "value" : 1015.3, "quality" : "1" },
"wind" : {
"direction" : { "angle" : 999, "quality" : "9" },
"type" : "9",
"speed" : { "rate" : 999.9, "quality" : "9" }
},
"visibility" : {
"distance" : { "value" : 999999, "quality" : "9" },
"variability" : { "value" : "N", "quality" : "9" }
},
"skyCondition" : {
"ceilingHeight" : { "value" : 99999, "quality" : "9", "determination" : "9" },
"cavok" : "N"
},
"sections" : [ "AG1" ],
"precipitationEstimatedObservation" : { "discrepancy" : "2", "estimatedWaterDepth" : 999 }
});
# db.weather_data.findOne(); # 查询数据
为了转换weather_data集合的数据,使用以下命令,创建中间临时集合temporarytimeseries:
db.weather_data.aggregate([
{
$addFields: {
metaData: {
"st": "$st",
"position": "$position",
"elevation": "$elevation",
"callLetters": "$callLetters",
"qualityControlProcess": "$qualityControlProcess",
"type": "$type"
}
},
},
{
$project: {
_id: 1,
ts: 1,
metaData: 1,
dataSource: 1,
airTemperature: 1,
dewPoint: 1,
pressure: 1,
wind: 1,
visibility: 1,
skyCondition: 1,
sections: 1,
precipitationEstimatedObservation: 1
}
},
{
$out: "temporarytimeseries"
}
]);
查询中间临时集合数据
db.temporarytimeseries.findOne();
{
"_id" : ObjectId("5553a998e4b02cf7151190b8"),
"ts" : ISODate("1984-03-05T13:00:00Z"),
"dataSource" : "4",
"airTemperature" : {
"value" : -3.1,
"quality" : "1"
},
"dewPoint" : {
"value" : 999.9,
"quality" : "9"
},
"pressure" : {
"value" : 1015.3,
"quality" : "1"
},
"wind" : {
"direction" : {
"angle" : 999,
"quality" : "9"
},
"type" : "9",
"speed" : {
"rate" : 999.9,
"quality" : "9"
}
},
"visibility" : {
"distance" : {
"value" : 999999,
"quality" : "9"
},
"variability" : {
"value" : "N",
"quality" : "9"
}
},
"skyCondition" : {
"ceilingHeight" : {
"value" : 99999,
"quality" : "9",
"determination" : "9"
},
"cavok" : "N"
},
"sections" : [
"AG1"
],
"precipitationEstimatedObservation" : {
"discrepancy" : "2",
"estimatedWaterDepth" : 999
},
"metaData" : {
"st" : "x+47600-047900",
"position" : {
"type" : "Point",
"coordinates" : [
-47.9,
47.6
]
},
"elevation" : 9999,
"callLetters" : "VCSZ",
"qualityControlProcess" : "V020",
"type" : "FM-13"
}
}
9.3,迁移数据到时序集合
要将数据从一个不属于时间序列类型的现有集合迁移到时间序列集合,使用mongodump 和 mongorestore.
当迁移或回填到时间序列集合时,应该始终按照从最老到最新的顺序插入文档。在例子中,mongodump以自然顺序导出文档,mongorestore的——maintainInsertionOrder选项保证文档的插入顺序相同。
# 导出temporarytimeseries集合数据,使用以下命令:
mongodump --authenticationDatabase=admin --uri="mongodb://admin:123456@127.0.0.1:27017/test" --collection=temporarytimeseries --out=D:\UserData\Administrator\Desktop\temporarytimeseries.bson
# 输出
2021-07-23T11:13:14.142+0800 writing test.temporarytimeseries to D:\UserData\Administrator\Desktop\temporarytimeseries.bson\test\temporarytimeseries.bson
2021-07-23T11:13:14.203+0800 done dumping test.temporarytimeseries (1 document)
# 导入数据(注意文件路径),使用以下命令:
mongorestore --authenticationDatabase=admin --uri="mongodb://admin:123456@127.0.0.1:27017/test" --collection=weathernew --noIndexRestore --maintainInsertionOrder D:\UserData\Administrator\Desktop\temporarytimeseries.bson\test\temporarytimeseries.bson
# 输出
2021-07-23T11:15:21.127+0800 checking for collection data in D:\UserData\Administrator\Desktop\temporarytimeseries.bson\test\temporarytimeseries.bson
2021-07-23T11:15:21.157+0800 restoring to existing collection test.weathernew without dropping
2021-07-23T11:15:21.158+0800 reading metadata for test.weathernew from D:\UserData\Administrator\Desktop\temporarytimeseries.bson\test\temporarytimeseries.metadata.json
2021-07-23T11:15:21.158+0800 restoring test.weathernew from D:\UserData\Administrator\Desktop\temporarytimeseries.bson\test\temporarytimeseries.bson
2021-07-23T11:15:21.282+0800 no indexes to restore
2021-07-23T11:15:21.283+0800 finished restoring test.weathernew (1 document, 0 failures)
2021-07-23T11:15:21.287+0800 1 document(s) restored successfully. 0 document(s) failed to restore.
注意: 确保使用--noIndexRestore选项,因为mongorestore无法在时间序列集合上创建索引,所以需要手动创建索引,如果需要.
10,在时间序列数据之上构建物化视图
好处:
archiving
analytics
为无法访问原始数据的团队提供数据访问便利
创建按需物化视图,使用$merge聚合管道阶段来转换和存储数据:
# 将使用基于weather集合的所有日平均温度创建或更新dailytemperatureaverages集合。
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" }
}
},
{
$merge: { into: "dailytemperatureaverages", whenMatched: "replace" }
}
]);