有一天,我在SO上回答question(接受为正确),但答案给我留下了很大的疑问。
不久,用户有一个包含此字段的表:
id INT PRIMARY KEY
dt DATETIME (with an INDEX)
lt DOUBLE
查询SELECT DATE(dt),AVG(lt) FROM table GROUP BY DATE(dt)
非常慢。
我们告诉他(部分)问题是使用 DATE(dt)作为字段和分组,但db在生产服务器上并且无法拆分该字段。
因此(使用触发器)插入另一个字段da DATE (with an INDEX)
,其中 DATE(dt)自动填充。查询SELECT da,AVG(lt) FROM table GROUP BY da
有点快,但是大约有8百万条记录需要大约60秒!!!
我试过我的电脑,最后我发现,删除字段 da 查询的索引只花了7秒,而在删除索引后使用 DATE(dt)花了13秒。登记/>
我一直以为用于分组的列的索引可以真正加快查询速度,而不是相反(慢8倍!!!)。
为什么?原因是什么?
非常感谢。
答案 0 :(得分:4)
因为您仍然需要读取索引+数据文件中的所有数据。由于您没有使用任何where
条件 - 您始终会拥有逐行访问所有数据的查询计划,而您无法对此执行任何操作。
如果性能对于此查询很重要并且经常执行 - 我建议将结果缓存到某个临时表中并每小时更新一次(每天等)。
为什么它变慢:因为索引数据已经排序,当mysql计算查询执行的成本时,它认为最好使用已排序的数据,然后对其进行分组,然后计算agregates。但在这种情况下并非如此。
答案 1 :(得分:1)
我认为这是因为这个或类似的MySQL错误:Index degrades sort performance and optimizer does not honor IGNORE INDEX
答案 2 :(得分:0)
我记得这个问题,因为我要回答这个问题,但却被其他事情分散了注意力。问题是他的表设计没有利用聚集的主键索引。
我会重新设计该表,创建一个复合聚簇主键,其中日期作为索引的主要部分。 sm_id字段仍然只是一个顺序的unsigned int,以保证唯一性。
drop table if exists speed_monitor;
create table speed_monitor
(
created_date date not null,
sm_id int unsigned not null,
load_time_secs double(10,4) not null default 0,
primary key (created_date, sm_id)
)
engine=innodb;
+------+----------+
| year | count(*) |
+------+----------+
| 2009 | 22723200 | 22 million
| 2010 | 31536000 | 31 million
| 2011 | 5740800 | 5 million
+------+----------+
select
created_date,
count(*) as counter,
avg(load_time_secs) as avg_load_time_secs
from
speed_monitor
where
created_date between '2010-01-01' and '2010-12-31'
group by
created_date
order by
created_date
limit 7;
-- cold runtime
+--------------+---------+--------------------+
| created_date | counter | avg_load_time_secs |
+--------------+---------+--------------------+
| 2010-01-01 | 86400 | 1.66546802 |
| 2010-01-02 | 86400 | 1.66662466 |
| 2010-01-03 | 86400 | 1.66081309 |
| 2010-01-04 | 86400 | 1.66582251 |
| 2010-01-05 | 86400 | 1.66522316 |
| 2010-01-06 | 86400 | 1.66859480 |
| 2010-01-07 | 86400 | 1.67320440 |
+--------------+---------+--------------------+
7 rows in set (0.23 sec)