【发布时间】:2011-04-19 18:18:37
【问题描述】:
我的应用程序做了很多事情是:
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,但没有任何明显的不同。
它看起来有点像this 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();
完成后,我观察到这种行为:
- 5.1.48
-
select count(distinct id) from x- 解释是:键:ix_a,额外:使用索引
- 运行时间不到 1.0 秒
-
select count(id) from x- 解释是:键:ix_a,额外:使用索引
- 运行时间不到 0.5 秒
-
- 5.5.11
-
select count(distinct id) from x- 解释是:键:PRIMARY,额外:使用索引进行分组
- 运行时间超过 7.0 秒
-
select count(id) from x- 解释是:键:ix_a,额外:使用索引
- 运行时间不到 0.5 秒
-
编辑:
如果我通过说修改 5.5 中的查询
select count(distinct id) from x force index (ix_a);
它运行得更快。索引 b 和 c 也有效(在不同程度上),甚至强制索引 PRIMARY 也有帮助。
【问题讨论】:
-
两张x表是不是一模一样?请在两个数据库上运行
SHOW CREATE TABLE x\G并发布结果。我对 ix_blahblahblah 索引特别感兴趣,该索引用于 5.1 但不是 5.5。 -
Ike:我不想发布我正在处理的实际代码,但我会尝试提出类似的问题来解决这个问题。
-
Ike:添加了简化示例!该表比我的实际数据要小一些(行少列),但 5.5 的性能下降仍然接近 10 倍。
-
5.1和5.5的配置一样吗?如果 5.5 对缓存和密钥存储等内容的限制更小,您会看到这样的结果。
-
马克:他们是一样的。当我安装 5.5 时,我手动复制了 5.1 配置中似乎对查询性能很重要的所有部分。刚才我复制了其余的选项,很好地衡量,然后重新启动,我看到的性能和以前一样。
标签: mysql nhibernate primary-key innodb distinct