提高Mysql查询的性能(很多行,时间戳“问题”)

时间:2011-04-28 11:52:14

标签: mysql performance

首先,我想说的是数据库的设计已经提供给我,并且我被要求提高查询的性能。我不应该修改表的字段(我相信将一些字符移动到int会提高性能......)。

该表有多行(我大约有14 * 10 ^ 6行,而且还在增长......)

我想提高像这样的查询的性能:

SELECT 
    timestampms/1000 as secs, 
    round(avg(data),2) as data, 
    FROM_UNIXTIME(timestampms/1000,'%m/%Y') as control 
FROM externalsensor 
WHERE (model='electric' and id=4 AND sensorid='TRI1VA' AND rawdata < 65535 ) 
    AND timestampms BETWEEN '1272454583000' AND '1303990583000' 
GROUP BY control 
ORDER BY secs ASC;

上述表格是:

CREATE TABLE `externalsensor` (
  `model` varchar(50) NOT NULL DEFAULT 'desconocido',
  `timestampms` char(13) DEFAULT NULL,
  `amtype` smallint(5) unsigned DEFAULT NULL,
  `id` smallint(5) unsigned DEFAULT NULL,
  `version` smallint(5) unsigned DEFAULT NULL,
  `interval` smallint(5) unsigned DEFAULT NULL,
  `counter` int(10) unsigned DEFAULT NULL,
  `sensorid` varchar(20) DEFAULT NULL,
  `rawdata` smallint(5) unsigned DEFAULT NULL,
  `data` decimal(20,10) DEFAULT NULL,
  KEY `temps` (`timestampms`),
  KEY `sensor` (`model`,`id`,`sensorid`,`timestampms`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1

解释的选择显示:

+----+-------------+----------------+-------+---------------+--------+---------+------+--------+----------------------------------------------+
| id | select_type | table          | type  | possible_keys | key    | key_len | ref  | rows   | Extra                                        |
+----+-------------+----------------+-------+---------------+--------+---------+------+--------+----------------------------------------------+
|  1 | SIMPLE      | externalsensor | range | temps,sensor  | sensor | 92      | NULL | 194443 | Using where; Using temporary; Using filesort |
+----+-------------+----------------+-------+---------------+--------+---------+------+--------+----------------------------------------------+

查询花了 20秒,我想知道是否有人看到了改进查询的方法。感谢任何建议:)

更新

经过所有令人敬畏的建议,以及这些家伙给我的MySql的这一课,我决定做以下事情:

因为我想要的是按月(一年),几周(一年)和一小时(一天)的数据的摘要,我决定创建一个< strong>摘要表,包含所有信息,并由cron中的脚本自动更新。

通过这样做,我不是在查询 200.000行,我相信它会有所改善。当然,我将考虑所有关于索引的建议,char-&gt; int以及这些人提出的所有建议。

我还没有开始开发,但是一旦完成并且我有了新的结果,我将与他们一起更新这篇文章。

我希望我能接受所有答案,但这是不可能的......谢谢大家。

5 个答案:

答案 0 :(得分:3)

尝试通过将计算存储在表中来从select中删除计算。

在您的情况下,timestampms/1000 as secsFROM_UNIXTIME(timestampms/1000,'%m/%Y') as control可以在 secs 字段和控制字段中预先计算,并填充ON INSERT和ON UPDATE的触发器即可。你不修改字段,你添加一些。

'control'用于GROUP BY,因此通过在此新控件列上添加索引可以获得很好的好处。 'secs'用于排序,因此同样适用。您甚至可能需要基于这两列的一些索引。

答案 1 :(得分:2)

timestampms的类型更改为bigint(15) unsigned - 您当前有5个表达式需要MySQL在计算之前将该字段从字符串转换为整数。正如Pentium10所建议的那样 - 做ORDER BY timestampms

此外,您无需在rawdata < 65535语句中指定WHERE,因为65535是无符号smallint的最大值。

答案 2 :(得分:2)

这里有几个问题:

  • 您正在按计算字段执行订单,这意味着您要对结果集进行排序,而不是从索引中进行选择。
  • 您正在为表中的每一行执行函数调用,这会降低速度并使用大量CPU。

修复它:

  • 使用批量更新或触发器预先计算所有字段
  • changerawdata test不等于65535
  • 使用where子句中的所有字段创建索引。

答案 3 :(得分:1)

按真实场(时间戳)排序,而非计算字段。

尝试使用“GROUP BY”timestampms / 1000“而不是”control“。

在mysql ui上,单独查询这些:

WHERE model ='electric'

WHERE id = 4

WHERE sensorid ='TRI1VA'

WHERE rawdata&lt; 65535

时间戳在'1272454583000'和'1303990583000'之间的时间戳

然后返回最少的行,请确保在第一个位置的字段上有索引。

这是一种粗略的方法,您可以从那里向索引添加字段。

SELECT列表中的计算表达式不会有任何区别。如果你需要,where子句中的“rawdata”就可以了。

你真的需要它返回194,000行吗?在任何情况下,获取许多将花费相当长的时间。

答案 4 :(得分:0)

根据某些条件(例如时间),您需要分区您需要的数据量。

您仍然可以通过索引进行改进。

SELECT timestampms / 1000                         AS secs, 
       Round(Avg(data), 2)                        AS data, 
       From_unixtime(timestampms / 1000, '%m/%Y') AS control 
FROM   externalsensor 
WHERE  ( model = 'electric' 
         AND id = 4 
         AND sensorid = 'TRI1VA' 
         AND rawdata < 65535 ) 
       AND timestampms BETWEEN '1272454583000' AND '1303990583000' 
GROUP  BY control 
ORDER  BY timestampms ASC; 

创建一个包含大量数据集的样本表,然后尝试使用此复合索引

index(model,id,sensorid,rawdata,timestampms,control,data)

回复解释输出。