我正在构建一个IOT应用程序,在该应用程序中,传感器数据已登录到SQL Server数据库的名为SENSOR_DATA
的表中。
SENSOR_DATA
表的列和数据类型在下面指定
ID BIGINT
SENSOR_ID BIGINT
READINGS_DATE DATETIME
READING DOUBLE
至少每个传感器每天将产生约600个读数,并将其记录到数据库中。
总共可以容纳1000个传感器。
这意味着每天大约会有1000 x 600 =“ 600,000” INSERTS
。
最常用的查询将是获取所有传感器的最新读数(基于DATETIME
列)。
我目前已使用相关查询来实现此目的。查询的构成方式使我强烈感觉这将是处理器和内存的饥饿。
我想出了一个工作,在下面对此进行解释:
LATEST_SENSOR_DATA
的表。SENSOR_DATA
表中时,更新LATEST_SENSOR_DATA
表中的相应值。使用这种技术,我只需要使用所需的传感器LATEST_SENSOR_DATA
来查询小得多的ID
表。
此解决方案听起来如何,还有其他解决方法吗?
2019年11月2日更新
大家好。感谢您的反馈意见。为我指出正确的方向非常有帮助。首先,我想说明一下,我没有建立生产数据库的经验。
我想提供更多有关数据库设计的信息。
正如Gordon Linoff正确建议的那样,存在一个传感器主表,其中包含有关传感器的一些元信息。这意味着sensor_data表中的“ sensor_id”列是外键列。
除了经常检索最新的传感器数据外,用户还将适度查询特定日期/周/月的特定传感器数据。
sensor_data表中的数据永远不会被用户更新或删除。 (出于存档目的,数据将以块的形式删除。)
可以保留过去三个月的数据。 现在,我已经阅读了一些索引,以及它们如何加快数据检索的速度以及维护索引的成本。
引起我注意的一种特殊类型的INDEX是“过滤索引”。有了这些,我可以每月在(readings_date,sensor_is)列上创建一个过滤索引。
这样做的好处是,我将拥有“小的”可管理索引,与整个表的一个大索引(全表索引)相比,该索引更易于维护。
有了这个解决方案,我相信我可能必须坚持我的原始计划,即维护一个last_sensor_data表。
现在我的问题是,这两种情况中哪种更好
仅创建过滤索引。使用latest_sensor_data表获取最新数据。
创建一个大的全表索引。使用全表索引查询最新数据。
戈登·利诺夫(Gordon Linoff)猜对了我用来检索最新数据的查询(他的答案中的第一个查询)也是正确的。我花了一段时间了解他的第二个查询,但是现在我明白了为什么这样的查询比我正在使用的查询好得多。谢谢。
PS:我花了一些时间才能理解他的表别名语法。我了解到“ AS”关键字是必需的,但实际上是可选的。
答案 0 :(得分:2)
800,000次插入非常重要。也就是说,平均每秒10次插入。
查询,例如:
select sd.*
from sensor_data sd
where sd.readings_date = (select max(sd2.readings_date)
from sensor_data sd2
where sd2.sensor_id = sd.sensor_id
);
是合理的。但是,即使在sensor_data(sensor_id, readings_date)
上有索引,也可能需要对整个表进行全面扫描。
可以通过将查询编写为以下内容来改善此问题:
select sd.*
from sensors s cross apply -- I assume you have such a table
(select top (1) sd.*
from sensor_data sd
where sd.sensor_id = s.sensor_id
order by sd.readings_date desc
) sd;
这应该使用索引为每个传感器获取适当的行。
您可以通过将sensor_date
添加到聚簇索引来加快此过程,以便所有最新行一起出现。实际上这可能是正确的,因此可能没有必要。这不是获取有关一个传感器的数据的考虑因素。
通过比较,使用触发器中的值添加新表将有所帮助。但是对于要运行的任何查询,您都需要平衡每天减少80万次插入的速度。
根据您的近期需求,您可能会发现使用定期的SQL Server代理作业将数据复制到另一个数据库(甚至在另一个服务器上)就足够了。然后使用该其他数据库来汇总“数据集市”中的数据。这样可以平衡“使用触发器降低插入速度”和应用程序的“快速响应需求”。
对于任何一种情况,您都需要进行测试,以查看哪种方法最适合您的情况。
答案 1 :(得分:2)
我想这是一个好消息,您不需要重复的数据表
首先我做了大量的测试数据
truncate table sensor_data;
WITH NOS AS (SELECT row_number() OVER (order by a.name) N FROM sys.all_objects a,sys.all_objects b)
insert into sensor_data
SELECT
row_number() over (order by qsens.n) ID,
qsens.n Sensor_ID,
dateadd(second,
qread.n,
dateadd(day,qd.n,CAST('20150101' as datetime))
) readings_date
, 456.255 + log(qd.n + qsens.n+qread.n) as reading
from nos as qsens, nos as qd , nos as qread
where qsens.n<=100 and qd.n<1300 and qread.n <=600;
select distinct S1.Sensor_ID into SENSORS from sensor_data S1;
(我已经完成了Gordon Linoff建议的传感器表)
然后我添加了一个索引
CREATE nonCLUSTERED INDEX [ClusteredIndex-20190204-151527] ON [dbo].[SENSOR_DATA]
(
[Sensor_ID] ASC,
[Readings_Date] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
GO
然后,以下查询非常有效地提供了每个传感器的最新信息。
select distinct S1.Sensor_ID,SQ.Reading,sq.Readings_Date,sq.ID from SENSORS S1
outer apply (SELECT TOP 1 * from sensor_data S2
WHERE s2.Sensor_ID = s1.Sensor_ID
ORDER BY S2.Readings_Date DESC) SQ;
没有'sensors'表和索引,它的运行情况非常糟糕。
注意:我尝试制作一个唯一传感器ID的索引视图,该视图也与查询配合得很好(解决了设置索引视图的一些问题之后)。但是,在文档中质疑了它们是否适合用于高度更新的表。
答案 2 :(得分:0)
您提到的方法是正确的,它给出了最错误的查询响应时间,而不是查询主表,这使所有传感器都能更好地读取,从而可以将最新的读取条目保留在单独的表中,或者可以使用您可能拥有的传感器主表。