[英]Optimizing unexplainably slow MySQL query
我在一个愚蠢的问题上失去了头发。 首先,我会解释它的目标是什么。 我每小时都会获取一组值并存储在数据库中。 这些值可以随时间增加或保持相等。 该查询提取最新的价值日益为最新的超过60天(我有一个由几周和几个月提取最新的值双胞胎查询,它们是相似的)。 查询是自解释的:
SELECT l.value AS value
FROM atable AS l
WHERE l.time = (
SELECT MAX(m.time)
FROM atable AS m
WHERE DATE(l.time) = DATE(m.time)
LIMIT 1
)
ORDER BY l.time DESC
LIMIT 60
看起来没什么特别的。 但它非常慢(> 30秒),考虑到time
是一个索引,表包含少于5000行。 而且我确定问题在于子查询。
noob错误在哪里?
更新1 :如果我使用SELECT m.time ... ORDER BY m.time DESC
避免使用MAX()
则情况相同。
更新2 :多次调用DATE()
函数似乎不是问题。 我试图创建一个计算字段day DATE
。 UPDATE atable SET day = DATE(time)
运行UPDATE atable SET day = DATE(time)
小于2秒。 修改后的查询,使用l.day = m.day
(无函数!),与之前完全相同的时间运行。
我看到的主要问题是在WHERE
子句中的表达式左侧使用DATE()
。 在WHERE
表达式的两侧使用函数DATE()
显式阻止MySQL在日期字段上使用索引。 相反,它必须扫描所有行以在每一行上应用该函数。
而不是这个:
WHERE DATE(l.time) = DATE(m.time)
尝试这样的事情:
WHERE l.time BETWEEN
DATE_SUB(m.date, INTERVAL TIME_TO_SEC(m.date) SECOND)
AND DATE_ADD(DATE_SUB(m.date, INTERVAL TIME_TO_SEC(m.date) SECOND), INTERVAL 86399 SECOND)
也许你知道一个更好的方法将m.date
变成像2012-02-09 00:00:00
和2012-02-09 23:59:59
这样的范围比上面的例子,但是这个想法是你想要的在这种情况下,将表达式的左侧保留为原始列名称l.time
,并在右侧以两个常量(或两个可转换为常量的表达式)的形式给出一个范围。
编辑
我正在使用您预先计算的day
字段:
SELECT *
FROM atable a
WHERE a.time IN
(SELECT MAX(time)
FROM atable
GROUP BY day
ORDER BY day DESC
LIMIT 60)
至少在这里,内部查询只运行一次,然后使用IN
cluase完成二进制搜索。 你仍在扫描表格,但只有一次,内部查询只运行一次的优势可能会产生巨大影响。
如果您知道每天都有值,则可以通过添加WHERE
子句,将其限制为过去60个日历日,并丢失LIMIT 60
来改进内部查询。 确保将day
和time
编入索引。
而不是使用MAX(m.time)在子选择中执行以下操作
SELECT m.time
FROM table AS m
WHERE DATE(l.time) = DATE(m.time)
ORDER BY m.time DESC
LIMIT 1
这可能有助于加快查询速度,因为它为查询解析器提供了另一种选择
然而另一件我注意到的是你正在使用DATE(l.time)和DATE(m.time),如果你的索引没有在DATE(m.time)创建,那么你将不会使用索引,因此可能导致缓慢。
根据反馈答案,如果条目按日期/时间顺序添加,直接与自动增量ID相关,谁关心TIME ...获取auto-inc号码进行精确,非模糊的连接
select
A1.AutoID,
A1.time,
A1.Value
from
( select date( A2.time ) as SingleDate,
max( A2.AutoID ) as MaxAutoID
from aTable A2
where date( A2.Time ) >= date( date_sub( now(), interval 60 day ))
group by date( A2.time ) ) into MaxPerDate
JOIN aTable A1
on MaxPerDate.MaxAutoID = A1.AutoID
order by
A1.AutoID DESC
您可以使用“explain”语句让mysql告诉您它正在做什么。
EXPLAIN SELECT l.value AS value
FROM table AS l
WHERE l.time = (
SELECT MAX(m.time)
FROM table AS m
WHERE DATE(l.time) = DATE(m.time) LIMIT 1
)
ORDER BY l.time DESC LIMIT 60
这应该至少可以让你深入了解哪里可以进一步了解。
如果您有time
索引,我建议按以下方式获取TOP 1
而不是MAX
:
SELECT l.value AS value
FROM table AS l
WHERE l.time = (
SELECT TOP 1 m.time
FROM table AS m
ORDER BY m.time DESC LIMIT 1
)
ORDER BY l.time DESC LIMIT 60
您的外部查询使用没有索引的filesort。 尝试更改为InnoDB引擎,看看它是否有所改进。
做快速测试:
mysql> show create table atable\G
*************************** 1. row ***************************
Table: atable
Create Table: CREATE TABLE `atable` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`t` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `t` (`t`)
) ENGINE=InnoDB AUTO_INCREMENT=51 DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
mysql> explain SELECT id FROM atable AS l WHERE l.t = ( SELECT MAX(m.t) FROM atable AS m WHERE DATE(l.t) = DATE(m.t) LIMIT 1 ) ORDER BY l.t DESC LIMIT 50;
+----+--------------------+-------+-------+---------------+------+---------+------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------------+-------+-------+---------------+------+---------+------+------+--------------------------+
| 1 | PRIMARY | l | index | NULL | t | 4 | NULL | 50 | Using where; Using index |
| 2 | DEPENDENT SUBQUERY | m | index | NULL | t | 4 | NULL | 50 | Using where; Using index |
+----+--------------------+-------+-------+---------------+------+---------+------+------+--------------------------+
2 rows in set (0.00 sec)
After changing to MyISAM:
mysql> explain SELECT id FROM atable AS l WHERE l.t = ( SELECT MAX(m.t) FROM atable AS m WHERE DATE(l.t) = DATE(m.t) LIMIT 1 ) ORDER BY l.t DESC LIMIT 50;
+----+--------------------+-------+-------+---------------+------+---------+------+------+-----------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------------+-------+-------+---------------+------+---------+------+------+-----------------------------+
| 1 | PRIMARY | l | ALL | NULL | NULL | NULL | NULL | 50 | Using where; Using filesort |
| 2 | DEPENDENT SUBQUERY | m | index | NULL | t | 4 | NULL | 50 | Using where; Using index |
+----+--------------------+-------+-------+---------------+------+---------+------+------+-----------------------------+
2 rows in set (0.00 sec)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.