Мое приложение делает изрядную сумму:
select count(distinct id) from x;
с id
первичным ключом для таблицы x
. С MySQL 5.1 (и 5.0) это выглядит так:
mysql> explain SELECT count(distinct id) from x;
+----+-------------+----------+-------+---------------+-----------------+---------+------+---------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+----------+-------+---------------+-----------------+---------+------+---------+-------------+
| 1 | SIMPLE | x | index | NULL | ix_blahblahblah | 1 | NULL | 1234567 | Using index |
+----+-------------+----------+-------+---------------+-----------------+---------+------+---------+-------------+
На InnoDB это не совсем блестяще, но и неплохо.
На этой неделе я пробовал MySQL 5.5.11, и был удивлен, увидев, что тот же запрос во много раз медленнее. С заполненным кешем это занимает около 90 секунд, по сравнению с 5 секундами ранее. План теперь выглядит так:
mysql> explain select count(distinct id) from x;
+----+-------------+----------+-------+---------------+---------+---------+------+---------+-------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+----------+-------+---------------+---------+---------+------+---------+-------------------------------------+
| 1 | SIMPLE | x | range | NULL | PRIMARY | 4 | NULL | 1234567 | Using index for group-by (scanning) |
+----+-------------+----------+-------+---------------+---------+---------+------+---------+-------------------------------------+
Один из способов сделать это снова быстро - это использовать select count(id) from x
, который безопасен, потому что id
является первичным ключом, но я прохожу некоторые уровни абстракции (например, NHibernate), которые делают это нетривиальным задача.
Я пытался analyze table x
, но это не имело заметного значения.
Это похоже на этот баг , хотя неясно, к каким версиям это относится, или что происходит (никто не трогал его в течение года, хотя это "серьезно / высоко / высоко").
Есть ли способ, кроме простого изменения моего запроса, заставить MySQL быть более умным в этом вопросе?
UPDATE:
В соответствии с просьбой, вот способ воспроизвести его, более или менее. Я написал этот сценарий SQL для генерации 1 миллиона строк фиктивных данных (на их выполнение требуется 10 или 15 минут):
delimiter $$
drop table if exists x;
create table x (
id integer unsigned not null auto_increment,
a integer,
b varchar(100),
c decimal(9,2),
primary key (id),
index ix_a (a),
index ix_b (b),
index ix_c (c)
) engine=innodb;
drop procedure if exists fill;
create procedure fill()
begin
declare i int default 0;
while i < 1000000 do
insert into x (a,b,c) values (1,"one",1.0);
set i = i+1;
end while;
end$$
delimiter ;
call fill();
Когда это сделано, я наблюдаю такое поведение:
- 5.1.48
select count(distinct id) from x
- EXPLAIN это: ключ: ix_a, Extra: использование индекса
- требуется менее 1,0 секунды для запуска
select count(id) from x
- EXPLAIN это: ключ: ix_a, Extra: использование индекса
- для запуска требуется менее 0,5 с
- 5.5.11
select count(distinct id) from x
- ОБЪЯСНЕНИЕ: ключ: ПЕРВИЧНЫЙ, Дополнительно: использование индекса для группировки по
- требуется более 7,0 секунд для запуска
select count(id) from x
- EXPLAIN: key: ix_a, Extra: использование индекса
- требуется менее 0,5 секунды для запуска
EDIT:
Если я изменю запрос в 5.5, сказав
select count(distinct id) from x force index (ix_a);
работает намного быстрее. Индексы b и c также работают (в разной степени), и даже форсирование индекса PRIMARY
помогает.