MySQL 5.5"select distinct"真的很慢

Ken*_*Ken 5 mysql nhibernate innodb distinct primary-key

我的应用程序做了相当多的事情之一是:

select count(distinct id) from x;
Run Code Online (Sandbox Code Playgroud)

使用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 |
+----+-------------+----------+-------+---------------+-----------------+---------+------+---------+-------------+
Run Code Online (Sandbox Code Playgroud)

在InnoDB上,这并不是很酷,但它也不错.

本周我正在尝试使用MySQL 5.5.11,并且惊讶地发现相同的查询速度慢很多倍.启动缓存后,与之前的5秒相比,大约需要90秒.该计划现在看起来像这样:

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) |
+----+-------------+----------+-------+---------------+---------+---------+------+---------+-------------------------------------+
Run Code Online (Sandbox Code Playgroud)

让它再次快速运行的一种方法是使用select count(id) from x,这是安全的,因为它id是一个主键,但我正在经历一些抽象层(如NHibernate),这使得这是一个非常重要的任务.

我试过analyze table x但它没有产生任何明显的差异.

它看起来有点像这个bug,虽然不清楚适用的版本是什么,或者发生了什么(没有人在一年内触及它,但它是"严重/高/高").

除了简单地改变我的查询之外,还有什么办法可以让MySQL变得更聪明吗?

更新:

根据要求,这是一种或多或少重现它的方法.我编写了这个SQL脚本来生成100万行虚拟数据(需要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();
Run Code Online (Sandbox Code Playgroud)

当它完成后,我会观察到这种行为:

  • 48年5月1日
    • select count(distinct id) from x
      • EXPLAIN是:key:ix_a,Extra:使用索引
      • 运行时间不到1.0秒
    • select count(id) from x
      • EXPLAIN是:key:ix_a,Extra:使用索引
      • 运行不到0.5秒
  • 5.5.11
    • select count(distinct id) from x
      • EXPLAIN是:key:PRIMARY,Extra:使用index by group-by
      • 运行需要7.0秒
    • select count(id) from x
      • EXPLAIN是:key:ix_a,Extra:使用索引
      • 运行不到0.5秒

编辑:

如果我通过说修改5.5中的查询

select count(distinct id) from x force index (ix_a);
Run Code Online (Sandbox Code Playgroud)

它运行得更快.索引b和c也有效(在不同程度上),甚至强制索引也有PRIMARY帮助.

Joe*_*lli 1

我不保证这会更好,但作为一种可能的解决方法,您可以尝试:

SELECT COUNT(*)
    FROM (SELECT id
              FROM x
              GROUP BY id) t
Run Code Online (Sandbox Code Playgroud)