Mysql 为什么索引会使查询变得非常慢?

Mysql 为什么索引会使查询变得非常慢?,mysql,Mysql,有一天,我回答了一个问题(被认为是正确的),但这个答案让我产生了极大的怀疑。 很快,用户就有了一个包含以下字段的表: id INT PRIMARY KEY dt DATETIME (with an INDEX) lt DOUBLE 查询selectdate(dt),AVG(lt)fromtablegroupbydate(dt)的速度非常慢。 我们告诉他(部分)问题是使用DATE(dt)作为字段和分组,但db位于生产服务器上,无法拆分该字段。 因此(带触发器)插入另一个字段da DATE(带索引

有一天,我回答了一个问题(被认为是正确的),但这个答案让我产生了极大的怀疑。
很快,用户就有了一个包含以下字段的表:

id INT PRIMARY KEY
dt DATETIME (with an INDEX)
lt DOUBLE
查询
selectdate(dt),AVG(lt)fromtablegroupbydate(dt)
的速度非常慢。 我们告诉他(部分)问题是使用DATE(dt)作为字段和分组,但db位于生产服务器上,无法拆分该字段。
因此(带触发器)插入另一个字段
da DATE(带索引)
自动填充DATE(dt)。查询
选择da,按da从表组中的平均值(lt)稍微快一点,但大约有8mln记录,大约需要60秒
我在我的电脑上试过,最后我发现,删除字段da查询上的索引只需7秒,而删除索引后使用日期(dt)只需13秒。
我一直认为用于分组的列上的索引可以真正加快查询速度,而不是相反(速度慢8倍!!!)。
为什么?原因是什么?

非常感谢。

因为您仍然需要从index+数据文件中读取所有数据。由于您没有使用任何
where
条件,因此您将始终拥有一个查询计划,该计划可以逐行访问所有数据,您对此无能为力

如果性能对这个查询很重要并且经常执行,我建议将结果缓存到某个临时表中,并每小时(每天等)更新一次


为什么它会变慢:因为索引中的数据已经排序,当mysql计算查询执行的成本时,它认为最好使用已经排序的数据,然后对其进行分组,然后计算一个拒绝。但事实并非如此。

我认为这是因为这个或类似的MySQL错误:

我记得这个问题,因为我要回答它,但却被其他问题分散了注意力。问题是他的表设计没有利用聚集主键索引

我会重新设计表,创建一个复合聚集主键,日期作为索引的前导部分。sm_id字段仍然只是一个连续的无符号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)

我认为这并不能解释为什么在删除索引后,他在查询中获得了性能提升。@zerkms:但MySql必须按索引列进行分组,所以我认为它应该更快地聚合关闭的记录。很明显,我所想的并不是现实中发生的事情:)但我想很好地理解它,因为我使用MySql,将来我可能会对我们的用户犯同样的错误(在我有很多记录之前可能不理解原因)@FractalizeR:当他有索引时-MySql需要使用索引来获取rowid(物理属性,数据文件中的偏移量)-然后转到数据文件,读取该行,然后返回索引,等等。相反,您可以按顺序读取数据文件。顺序读取比大量重复的随机读取更快。这不是错误,这是预期行为,是由优化器计算的查询成本评估不太准确造成的。@Marco:即使它有“关闭”值附近(因为它们已排序)-mysql仍然需要转到数据文件以计算聚合值。删除
AVG(字段)
并尝试查询:
按da从表组中选择da
。它应该很快。@FractalizeR:ps:是的,看起来像是那个bug,对不起。但是根据那里的评论它已经修复了:-是的,我认为这种行为有相同的根源:错误的CBO计算。@FractalizeR:在我的电脑上我有MySQL5.5Wow,我甚至不知道它,谢谢。总之是的,他们在谈论MySQL 5.1,他们说已经提交了一个补丁…是的,但它的日期字段不是日期,而是日期时间,事实上他必须使用日期(…)。无论如何,在插入字段da之后,你会将PK声明为复合(id,da)?嗯,也许你是对的,但是相信我:如果db是我的,我就不应该考虑这个问题..我会在不知道原因的情况下遇到与其他用户相同的问题;)好的,那么继续添加一个新的日期字段,但将该新字段作为集群复合键的一部分