繁体   English   中英

每N秒最多选择1行

[英]Select no more than 1 row every N seconds

我有几个MySQL表,用于存储来自传感器的温度数据。 传感器每分钟报告一次,并且有数十个传感器(并且还在不断增加)。 表格已迅速增长到数百万行,并将继续增长。 这两个相关的表是datadata_temperature

data表的结构如下:

data_id bigint(20) unsigned NOT NULL AUTO_INCREMENT
created timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
sensor_id int(10) unsigned NOT NULL

data_temperature表的结构如下:

temperature_id bigint(20) unsigned NOT NULL AUTO_INCREMENT
created timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
data_id bigint(20) unsigned NOT NULL
x_id varchar(32) DEFAULT NULL
x_sn varchar(16) DEFAULT NULL
x_unit char(1) DEFAULT NULL
x_value` decimal(6,2) DEFAULT NULL

由于每个传感器每分钟报告一次,因此每个传感器每天应该大约有1440行。 但是,数据有时会出现间隔,有时持续几分钟,有时持续更长的时间。

我需要选择数据样本以显示在图形上。 图形为600像素宽。 虽然图表的时间范围是可变的(有时是每日图表,有时是每周,有时是每年,等等),但图表的像素宽度是固定的。

最初,我将选择时间范围内的行数,然后将其除以600得到X ,然后选择data_id MOD X = 0的行。 但是,除非只有一个传感器向该表报告,否则这将无法正常工作。 由于有许多传感器,因此会产生很多间隙。 为了补偿,我提取了比所需更多的数据,并过度填充了图表以确保没有空洞。

人口过多会导致浏览器中的渲染时间变慢。 但是现在,即使SELECT COUNT()也是服务器端运行缓慢的主要原因,在data表上运行大约需要5-6秒。

理想情况下,我想从表中选择数据,以使给定窗口中的数据点不超过一个(如果没有数据,则为零)。 窗口是在图形中查看的总时间范围除以图形的像素宽度。 因此,查看600px宽的每日图表的计算方式如下:

86400 seconds per day / 600 pixels = 144-second window

因此,我希望每144秒不超过一个数据点。 到目前为止,这是我提出的查询:

SELECT data_temperature.data_id, data_temperature.created,
       ROUND( data_temperature.x_value, 1 ) AS temperature
  FROM data_temperature
         INNER JOIN data
                 ON data_temperature.data_id = data.data_id
 WHERE data.sensor_id = :sensor_id
   AND data.created BETWEEN :dt_start AND :dt_end
 GROUP BY ROUND( UNIX_TIMESTAMP( data_temperature.created ) / 144 )
 ORDER BY data.created, data.data_id

此查询的改进之处在于它返回了正确的数据,而且运行时间约为3.6秒。 这仍然比我真正想要的要慢得多。 因此,我想知道是否还有其他想法可以通过更有效的查询来完成此任务。

注意:即使看起来不正确,也有充分的理由将datadata_temperature表分开,即使它们之间的关系为1比1。 当我修改查询和结构以使所有内容都在一个表中时,它不会缩短查询时间。 因此,我认为拥有两个表不会对性能产生负面影响。

根据@Kevin Nelson的回复进行更新以澄清

不是GROUP BY这么慢,而是WHERE子句中的BETWEEN慢。 如果我删除了它,它的运行速度会更快,但是当然会返回错误的结果。 如果我执行像这样的简单查询:

SELECT data.data_id, data.created
  FROM data
 WHERE data.created BETWEEN :dt_start AND :dt_end

这也很慢。 created列已建立索引,因此我不确定为什么。 我确实知道dt_startdt_end之间的范围dt_end ,所需的时间dt_end慢。 一日范围大约需要半秒钟。 一个星期的时间大约需要10秒钟。

如果我对整体问题的理解是错误的,我深表歉意,但这听起来像是您在询问如何在选择行时优化表以获得最佳速度,因为从我所能看到的所有方面来看,您正在使用的GROUP BY都可以正常工作。 如果您的where条件针对索引列,则GROUP BY不应明显降低它的速度。

但是,您可以做一些事情来潜在地加速表查询:

1)在InnoDB表中,使主键成为sensor_id和创建的PRIMARY KEY (created,sensor_id) InnoDB使用聚簇索引作为主键,因此它不必搜索索引然后查找数据。 但是,如果可能,您要确保按主键的顺序插入行,以便可以将其放在最后。

2)使用表分区。 每月制作一个分区或其他一些时间度量将创建可以独立搜索的单独文件。 您只需要确保使用WHERE子句中的分区列,否则它将必须搜索每个文件。

http://dev.mysql.com/doc/refman/5.6/en/partitioning.html

[基于评论和Q更新的更新]

相信我,我比您更了解您的模型。 我几乎从事同一行业。 对于我目前的工作,我们每月有大约7,000万条恒温器记录,并且该记录正在迅速增长。 我们仅捕获每5分钟的数据,而不是每分钟。 我们总共有超过10亿条记录。 分区(手动或使用MySQL的内置分区)将月份分成自己的文件,因此任何给定的搜索仅需遍历给定月份的数据,而不是整个数据库。 因此,我不确定您为什么会认为分区不可扩展。 分区的全部重点是可伸缩性。

我唯一想到的另一个想法是每个传感器每个月都有一个NoSQL文件,这可能是最终的速度,但是我对NoSQL的了解还不够,还不了解所有的来龙去脉。

但是无论如何对于MySQL来说,使用我在InnoDB表上提到的7000万条记录(主键为(macAddress,timestamp))来获取价值2天的条目(576条记录)需要0.140秒。 我的本地计算机(慢得多的计算机)对同一查询只花费0.187秒。 如前所述,由于主键是聚簇索引,因此它与数据一起使用...因此,数据实际上是由mac,timestamp排序的。 因此,当找到索引时,就会找到数据。 使用标准的MySQL索引,您的代码必须找到将其指向数据的索引,然后必须分别获取数据,这会增加时间。

如果您使用的是MySQL工作台,我相信这是Duration / Fetch之间的区别。 如果您看到持续时间过长,则无法找到数据。 如果您看到持续时间短且获取次数很高,那么(我认为,但不是完全确定)它将快速找到数据的索引,但是获取它要花费时间,因为它会搜索所有这些指针位置。 当我在聚集索引上搜索时,提取时间为0.031秒。

不管您是否按照建议使用聚集索引,最后都需要对查询执行EXPLAIN SELECT... ,并确保它实际上在使用所需的索引。 如果不是,则需要找出原因。 至少,如果您没有它,我将创建索引:

INDEX bySensorAndTime (sensor_id, created)

这样,MySQL只需为您的查询使用一个索引,因为-我猜-您将始终在WHERE搜索这两个字段。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM