在快速增长的SQL Server数据库表中搜索最新数据

时间:2019-02-04 14:16:16

标签: c# sql sql-server entity-framework

我正在构建一个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日更新

大家好。感谢您的反馈意见。为我指出正确的方向非常有帮助。首先,我想说明一下,我没有建立生产数据库的经验。

我想提供更多有关数据库设计的信息。

  1. 正如Gordon Linoff正确建议的那样,存在一个传感器主表,其中包含有关传感器的一些元信息。这意味着sensor_data表中的“ sensor_id”列是外键列。

  2. 除了经常检索最新的传感器数据外,用户还将适度查询特定日期/周/月的特定传感器数据。

  3. sensor_data表中的数据永远不会被用户更新或删除。 (出于存档目的,数据将以块的形式删除。)

  4. 可以保留过去三个月的数据。 现在,我已经阅读了一些索引,以及它们如何加快数据检索的速度以及维护索引的成本。

引起我注意的一种特殊类型的INDEX是“过滤索引”。有了这些,我可以每月在(readings_date,sensor_is)列上创建一个过滤索引。
这样做的好处是,我将拥有“小的”可管理索引,与整个表的一个大索引(全表索引)相比,该索引更易于维护。
有了这个解决方案,我相信我可能必须坚持我的原始计划,即维护一个last_sensor_data表。

现在我的问题是,这两种情况中哪种更好

  1. 仅创建过滤索引。使用latest_sensor_data表获取最新数据。

  2. 创建一个大的全表索引。使用全表索引查询最新数据。

戈登·利诺夫(Gordon Linoff)猜对了我用来检索最新数据的查询(他的答案中的第一个查询)也是正确的。我花了一段时间了解他的第二个查询,但是现在我明白了为什么这样的查询比我正在使用的查询好得多。谢谢。

PS:我花了一些时间才能理解他的表别名语法。我了解到“ AS”关键字是必需的,但实际上是可选的。

3 个答案:

答案 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)

您提到的方法是正确的,它给出了最错误的查询响应时间,而不是查询主表,这使所有传感器都能更好地读取,从而可以将最新的读取条目保留在单独的表中,或者可以使用您可能拥有的传感器主表。