我有这个问题:
SELECT ROUND(AVG(temp)*multT + conT,2) as temp,
FLOOR(timestamp/$secondInterval) as meh
FROM sensor_locass
LEFT JOIN sensor_data USING(sensor_id)
WHERE sensor_id = '$id'
AND project_id = '$project'
GROUP BY meh
ORDER BY timestamp ASC
目的是选择绘制图形的数据,我使用一个像素值的数据的平均值来使图表忠实于数据。
到目前为止,优化包括添加索引,在MyISAM和InnoDB之间切换,但没有运气。
由于时间间隔随图形缩放和数据收集周期而变化,因此无法为GROUP BY
语句创建单独的列,但查询速度很慢。有没有人有优化此查询或表格的想法,以便更快地进行此分组,我目前在timestamp
,sensor_id
和project_id
列,timestamp
索引上有一个索引然而,没有使用。
使用查询运行explain extended
时,我得到以下内容:
1 SIMPLE sensor_locass ref sensor_id_lookup,project_id_lookup sensor_id_lookup 4 const 2 100.00 Using where; Using temporary; Using filesort
1 SIMPLE sensor_data ref idsensor_lookup idsensor_lookup 4 webstech.sensor_locass.sensor_id 66857 100.00
sensor_data
表目前包含270万个数据点,这只是我最终必须处理的数据量的一小部分。任何有用的想法,评论或解决方案都是最受欢迎的
编辑表格定义:
CREATE TABLE `sensor_data` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`gateway_id` int(11) NOT NULL,
`timestamp` int(10) NOT NULL,
`v1` int(11) NOT NULL,
`v2` int(11) NOT NULL,
`v3` int(11) NOT NULL,
`sensor_id` int(11) NOT NULL,
`temp` decimal(5,3) NOT NULL,
`oxygen` decimal(5,3) NOT NULL,
`batVol` decimal(4,3) NOT NULL,
PRIMARY KEY (`id`),
KEY `gateway_id` (`gateway_id`),
KEY `time_lookup` (`timestamp`),
KEY `idsensor_lookup` (`sensor_id`)
) ENGINE=MyISAM AUTO_INCREMENT=2741126 DEFAULT CHARSET=latin1
CREATE TABLE `sensor_locass` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`project_id` int(11) NOT NULL,
`sensor_id` int(11) NOT NULL,
`start` date NOT NULL,
`end` date NOT NULL,
`multT` decimal(6,3) NOT NULL,
`conT` decimal(6,3) NOT NULL,
`multO` decimal(6,3) NOT NULL,
`conO` decimal(6,3) NOT NULL,
`xpos` decimal(4,2) NOT NULL,
`ypos` decimal(4,2) NOT NULL,
`lat` decimal(9,6) NOT NULL,
`lon` decimal(9,6) NOT NULL,
`isRef` tinyint(1) NOT NULL,
PRIMARY KEY (`id`),
KEY `sensor_id_lookup` (`sensor_id`),
KEY `project_id_lookup` (`project_id`)
) ENGINE=MyISAM AUTO_INCREMENT=238 DEFAULT CHARSET=latin1
答案 0 :(得分:2)
尽管每个人都有答案,但更改主键以优化238行表格上的搜索并不会改变任何内容,尤其是当EXPLAIN显示单个键将搜索范围缩小到两行时。将timestamp
添加到sensor_data
上的主键将无法正常工作,因为没有任何内容正在查询时间戳,只计算它(除非你可以像galymzhan建议的那样限制时间戳值)。
哦,你可以在你的查询中删除LEFT
,因为project_id
上的匹配无论如何都会使它无关紧要(但不会减慢任何速度)。如果这些变量来自客户输入以避免$project_id = "'; DROP TABLES; --"类型sql注入攻击,请不要将变量直接插入到查询中。
调整堆大小可能会有一段时间,但如果需要扩展,则必须继续调整它。
答案vdrmrt建议可能有效,但是你需要用$ secondInterval的每一个可能值填充你的聚合表,考虑到你说你需要的灵活性,我假设这个值不是很合理。同样,您可以考虑rrdtool,直接使用它或以与它相同的方式修改数据。我具体指的是它将原始数据保留给定的一段时间(通常是几天),然后在越来越大的时间段内将数据点平均在一起。最终结果是您可以放大最近一段时间的高细节,但如果再往后看,数据已经被有效地有损压缩到大部分时间内的平均值(例如,每天一个数据点,一天,一个星期每分钟一个数据点,一个月每小时一个数据点,等等。您可以最初自定义这些平均值,但除非您同时保留原始数据和汇总数据,否则您将无法返回并进行调整。特别是,您无法动态放大某些较旧的任意点的高细节(例如查看六个月前发生的1小时的每秒数据)。
因此,根据您的要求,您必须决定这些限制是否合理。
如果没有,我会争辩说你正试图在MySQL中做一些不是为它设计的东西。我建议拉出你需要的原始数据,并在php中取平均值,而不是在你的查询中。正如已经指出的那样,查询需要很长时间的主要原因是因为GROUP BY
子句强制mysql处理内存中的所有数据,但由于它的数据太多,它实际上将该数据临时写入磁盘。 (因此using filesort
)。但是,就可以在php中使用多少内存而言,你有更多的灵活性。此外,由于您要组合附近的行,您可以逐行拉出数据,将其组合在一起,从而永远不需要在PHP进程中将所有行保留在内存中。然后,您可以删除GROUP BY
并避免使用filesort。请改用ORDER BY timestamp
,如果mysql没有正确优化,请确保使用FORCE INDEX FOR ORDER BY (timestamp)
答案 1 :(得分:1)
我建议您找到表的自然主键并切换到InnoDB。这可以猜测您的数据是什么样的:
sensor_data:
PRIMARY KEY (sensor_id, timestamp)
sensor_locass:
PRIMARY KEY (sensor_id, project_id)
InnoDB将以这种方式订购所有数据,因此您可能会SELECT
在一起的行将在磁盘上。我认为你是小伙伴总会造成一些麻烦。如果您可以将其保持在切换到文件排序(tmp_table_size
和max_heap_table_size
)的大小以下,那么速度会快得多。
您通常会返回多少行?现在需要多长时间?
答案 2 :(得分:0)
正如Joshua建议的那样,你应该将(sensor_id,project_id)定义为sensor_locass表的主键,因为目前表中每列有2个独立的索引。根据mysql文档,SELECT将仅从它们中选择一个索引(最具限制性,找到较少的行),而主键允许使用两个列来索引数据。
然而,EXPLAIN显示MySQL在连接表上检查了66857行,因此您也应该以某种方式对其进行优化。也许您可以在给定的时间间隔内查询传感器数据,例如timestamp BETWEEN (begin, end)
?
答案 3 :(得分:0)
我同意第一步应该是将sensor_id,project_id定义为sensor_locass的主键。 如果这还不够,并且您的数据是相对静态的,您可以创建一个聚合表,您可以每天刷新,而不是从那里查询。 您还需要做的是为secondInterval定义一个范围,将其存储在新表中,并将该字段添加到聚合表的主键中。
填充聚合表的查询将是这样的:
INSERT INTO aggregated_sensor_data (sensor_id,project_id,secondInterval,timestamp,temp,meh)
SELECT
sensor_locass.sensor_id,
sensor_locass.project_id,
secondInterval,
timestamp,
ROUND(AVG(temp)*multT + conT,2) as temp,
FLOOR(timestamp/secondInterval) as meh
FROM
sensor_locass
LEFT JOIN sensor_data
USING(sensor_id)
LEFT JOIN secondIntervalRange
ON 1 = 1
WHERE
sensor_id = '$id'
AND
project_id = '$project'
GROUP BY
sensor_locass.sensor_id,
sensor_locass.project_id,
meh
ORDER BY
timestamp ASC
您可以使用此查询提取聚合数据:
SELECT
temp,
meh
FROM
aggregated_sensor_data
WHERE
sensor_id = '$id'
AND project_id = '$project'
AND secondInterval = $secondInterval
ORDER BY
timestamp ASC
答案 4 :(得分:0)
如果您想使用时间戳索引,则必须明确告知使用该索引。 MySQL 5.1 支持USE INDEX FOR ORDER BY/FORCE INDEX FOR ORDER BY
。在这里查看http://dev.mysql.com/doc/refman/5.1/en/index-hints.html