物联网应用的数据库设计
Database design for IoT application
我们的应用程序显示客户远程设备的近实时物联网数据(最多 5 分钟间隔)。
最初的试点项目在 SQL Server 2008 数据库上的简单“测量”table 中始终存储每个设备读数。
table 看起来像这样:
Measurements: (DeviceId, Property, Value, DateTime)
.
在一两年内,每个设备 table 中可能会有 100,000 条记录,查询通常分为两类:
- “设备最新值”(95% 的查询):仅查看最新值
- “设备每日快照”(5% 的查询):查看每天的单个代表值
我们现在正在扩展到 5000 台设备。 Measurements
table 现在很小,但很快就会达到 50 亿条左右的记录,仅针对这 5000 台设备。
该应用程序是读取密集型应用程序,经常 运行 查询特别是查看“设备最新值”。
[编辑 #1:减少基于意见的]
我们可以使用哪些数据库设计技术来优化“最新”物联网值的快速读取,给定具有多年“历史”物联网价值的大 table?
我们团队的一个建议是将 MeasurementLatest
和 MeasurementHistory
存储为两个单独的 table。
[编辑#2:回应反馈]
在我们的测试数据库中,有 5000 万条记录,并应用了以下索引:
CREATE NONCLUSTERED INDEX [IX_Measurement_DeviceId_DateTime] ON Measurement (DeviceId ASC, DateTime DESC)
典型的“获取设备最新值”查询(如下所示)仍然需要超过 4,000 毫秒才能执行,这对于我们的需求来说太慢了:
SELECT DeviceId, Property, Value, DateTime
FROM Measurements m
WHERE m.DateTime = (
SELECT MAX(DateTime)
FROM Measurements m2
WHERE m2.DeviceId = m.DeviceId)
这是一个非常广泛的问题 - 因此,您不太可能得到明确的答案。
但是,我也遇到过类似的情况,我会运行通过我的思考和最终的方法。总而言之——我做了选项 B,但在某种程度上反映了选项 A:我使用过滤索引 'mimic' 单独的较小 table.
我最初的想法是有两个 table - 一个用于大多数报告的 'latest data only',然后一个具有所有历史值的 table。另一种方法是有两个 tables - 一个包含所有记录,一个包含最新记录。
插入新行时,通常需要更新至少两行,如果不是更多(取决于它的存储方式)。
相反,我选择了一条稍微不同的路线
- 将所有数据合为一体table
- 在那个 table 上,添加一个新列 'Latest_Flag'(位,NOT NULL,DEFAULT 1)。如果它是 1 那么它就是最新的值;否则它是历史的
- 在 table 上有一个 filtered index,它具有所有列(具有适当的列顺序)和过滤器 Latest_Flag = 1
- 此筛选索引类似于 table 的第二个副本,仅包含最新行
- 插入过程因此在事务中有两个步骤
- 'Unflag' 该设备的最后一个 Latest_Flag,等等
- 插入新行
它仍然使写入速度稍慢(因为它需要进行多个行更新以及索引更新)但从根本上说,它会为以后的读取进行预计算。
但是,当从 table 读取时,您需要指定 WHERE Latest_Flag = 1
。或者,您可能希望将其放入视图或类似视图中。
对于过滤后的索引,可能是这样的
CREATE INDEX ix_measurements_deviceproperty_latest
ON Measurements (DeviceId, Property)
INCLUDE (Value, DateTime, Latest_Flag)
WHERE (Latest_Flag = 1)
注意 - 另一个版本可以在触发器中完成,例如,当插入新行时,它会使之前的任何行无效(设置 Latest_Flag = 0)。这意味着您不需要进行两步插入;但是你确实依赖于 business/processing 触发器中的逻辑。
我们的应用程序显示客户远程设备的近实时物联网数据(最多 5 分钟间隔)。
最初的试点项目在 SQL Server 2008 数据库上的简单“测量”table 中始终存储每个设备读数。 table 看起来像这样:
Measurements: (DeviceId, Property, Value, DateTime)
.
在一两年内,每个设备 table 中可能会有 100,000 条记录,查询通常分为两类:
- “设备最新值”(95% 的查询):仅查看最新值
- “设备每日快照”(5% 的查询):查看每天的单个代表值
我们现在正在扩展到 5000 台设备。 Measurements
table 现在很小,但很快就会达到 50 亿条左右的记录,仅针对这 5000 台设备。
该应用程序是读取密集型应用程序,经常 运行 查询特别是查看“设备最新值”。
[编辑 #1:减少基于意见的]
我们可以使用哪些数据库设计技术来优化“最新”物联网值的快速读取,给定具有多年“历史”物联网价值的大 table?
我们团队的一个建议是将 MeasurementLatest
和 MeasurementHistory
存储为两个单独的 table。
[编辑#2:回应反馈]
在我们的测试数据库中,有 5000 万条记录,并应用了以下索引:
CREATE NONCLUSTERED INDEX [IX_Measurement_DeviceId_DateTime] ON Measurement (DeviceId ASC, DateTime DESC)
典型的“获取设备最新值”查询(如下所示)仍然需要超过 4,000 毫秒才能执行,这对于我们的需求来说太慢了:
SELECT DeviceId, Property, Value, DateTime
FROM Measurements m
WHERE m.DateTime = (
SELECT MAX(DateTime)
FROM Measurements m2
WHERE m2.DeviceId = m.DeviceId)
这是一个非常广泛的问题 - 因此,您不太可能得到明确的答案。
但是,我也遇到过类似的情况,我会运行通过我的思考和最终的方法。总而言之——我做了选项 B,但在某种程度上反映了选项 A:我使用过滤索引 'mimic' 单独的较小 table.
我最初的想法是有两个 table - 一个用于大多数报告的 'latest data only',然后一个具有所有历史值的 table。另一种方法是有两个 tables - 一个包含所有记录,一个包含最新记录。
插入新行时,通常需要更新至少两行,如果不是更多(取决于它的存储方式)。
相反,我选择了一条稍微不同的路线
- 将所有数据合为一体table
- 在那个 table 上,添加一个新列 'Latest_Flag'(位,NOT NULL,DEFAULT 1)。如果它是 1 那么它就是最新的值;否则它是历史的
- 在 table 上有一个 filtered index,它具有所有列(具有适当的列顺序)和过滤器 Latest_Flag = 1
- 此筛选索引类似于 table 的第二个副本,仅包含最新行
- 插入过程因此在事务中有两个步骤
- 'Unflag' 该设备的最后一个 Latest_Flag,等等
- 插入新行
它仍然使写入速度稍慢(因为它需要进行多个行更新以及索引更新)但从根本上说,它会为以后的读取进行预计算。
但是,当从 table 读取时,您需要指定 WHERE Latest_Flag = 1
。或者,您可能希望将其放入视图或类似视图中。
对于过滤后的索引,可能是这样的
CREATE INDEX ix_measurements_deviceproperty_latest
ON Measurements (DeviceId, Property)
INCLUDE (Value, DateTime, Latest_Flag)
WHERE (Latest_Flag = 1)
注意 - 另一个版本可以在触发器中完成,例如,当插入新行时,它会使之前的任何行无效(设置 Latest_Flag = 0)。这意味着您不需要进行两步插入;但是你确实依赖于 business/processing 触发器中的逻辑。