对物联网 (IoT) 数据进行建模
物联网 (IoT) 是由连接到互联网的物理对象组成的网络。其中许多设备,如传感器,都会生成数据。
如需有效存储和检索这些数据,可以使用存储桶模式。
桶模式(Bucket Pattern)
组织物联网数据的常用方法是将数据分组到存储桶中。存储桶组织特定的数据组以提供帮助:
发现历史趋势,
预测未来趋势,以及
优化存储使用情况。
对数据进行分组的常见参数包括:
时间
数据源(如果有多个数据集)
客户
数据类型(例如,财务数据中的交易类型)
注意
从MongoDB 5.0开始, 时间序列集合是时间序列数据的推荐集合类型。 请勿将存储桶模式与时间序列集合结合使用,因为这会降低性能。
以一个集合为例,其中存储有从传感器获得的温度数据。传感器每分钟记录一次温度并将数据存储在名为 temperatures
的集合中:
// temperatures collection { "_id": 1, "sensor_id": 12345, "timestamp": ISODate("2019-01-31T10:00:00.000Z"), "temperature": 40 } { "_id": 2, "sensor_id": 12345, "timestamp": ISODate("2019-01-31T10:01:00.000Z"), "temperature": 40 } { "_id": 3, "sensor_id": 12345, "timestamp": ISODate("2019-01-31T10:02:00.000Z"), "temperature": 41 } ...
这种方法在数据和索引大小方面不能很好地扩展。 例如,如果应用程序需要对sensor_id
和timestamp
字段建立索引,则需要对传感器的每个传入读数建立索引以提高性能。
您可以利用文档模型,将数据分组存储到文档中,以保存特定时间段的测量值。考虑以下更新模式,将每分钟采集的读数分成一小时一组:
{ "_id": 1, "sensor_id": 12345, "start_date": ISODate("2019-01-31T10:00:00.000Z"), "end_date": ISODate("2019-01-31T10:59:59.000Z"), "measurements": [ { "timestamp": ISODate("2019-01-31T10:00:00.000Z"), "temperature": 40 }, { "timestamp": ISODate("2019-01-31T10:01:00.000Z"), "temperature": 40 }, ... { "timestamp": ISODate("2019-01-31T10:42:00.000Z"), "temperature": 42 } ], "transaction_count": 42, "sum_temperature": 1783 }
此更新的模式提高了可扩展性并反映了应用程序实际使用数据的方式。用户可能不会查询特定的温度读数,而有可能会查询一小时或一天内的温度行为。存储桶模式通过将数据分组到统一的时间段来帮助简化这些查询。
结合计算模式和存储桶模式
示例文档包含两个计算字段: transaction_count
和sum_temperature
。 如果应用程序经常需要检索给定小时内的温度总和,则计算总和的运行总计可以帮助节省应用程序资源。 这种计算模式方法无需在每次请求数据时计算总和。
使用预聚合的 sum_temperature
和 transaction_count
值,可以执行进一步计算,例如特定存储桶的平均温度 (sum_temperature
/ transaction_count
)。用户更有可能在应用程序中查询下午 2:00 到 3:00 之间的平均温度,而不是查询下午 2:03 的具体温度。将某些值组织到存储桶中并预先计算,将使应用程序能够更轻松地提供该信息。
MongoDB 中的时间表示
默认情况下,MongoDB 以 UTC 格式存储时间,并将任何本地时间表示形式转换为这种格式。必须操作或报告某些未修改的本地时间值的应用程序可以将时区与 UTC 时间戳一起存储,并按照其应用程序逻辑计算原始本地时间。
例子
在 MongoDB Shell 中,您可以存储当前日期和当前客户端相对于 UTC 的偏移量。
var now = new Date(); db.data.save( { date: now, offset: now.getTimezoneOffset() } );
可以通过应用保存的偏移量来重建原始本地时间:
var record = db.data.findOne(); var localNow = new Date( record.date.getTime() - ( record.offset * 60000 ) );