2011-09-29 66 views
10

我有以下MySQL表(简体):为什么在MySQL中删除这个索引加快了我的查询100x?

CREATE TABLE `track` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `title` varchar(256) NOT NULL, 
    `is_active` tinyint(1) NOT NULL, 
    PRIMARY KEY (`id`), 
    KEY `is_active` (`is_active`, `id`) 
) ENGINE=MyISAM AUTO_INCREMENT=7495088 DEFAULT CHARSET=utf8 

的“IS_ACTIVE”列标志着我想在最忽略行,但不是全部,我的查询。我有一些查询会定期从这张表中读取块。其中一个看起来像这样:

SELECT id,title from track where (track.is_active=1 and track.id > 5580702) ORDER BY id ASC LIMIT 10; 

该查询需要一分钟才能执行。下面是执行计划:

> EXPLAIN SELECT id,title from track where (track.is_active=1 and track.id > 5580702) ORDER BY id ASC LIMIT 10; 
+----+-------------+-------+------+----------------+--------+---------+-------+---------+-------------+ 
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra  | 
+----+-------------+-------+------+----------------+--------+---------+-------+---------+-------------+ 
| 1 | SIMPLE  | t  | ref | PRIMARY,is_active | is_active | 1  | const | 3747543 | Using where | 
+----+-------------+-------+------+----------------+--------+---------+-------+---------+-------------+ 

现在,如果我告诉MySQL忽略“IS_ACTIVE人指数,查询发生瞬间。

> EXPLAIN SELECT id,title from track IGNORE INDEX(is_active) WHERE (track.is_active=1 AND track.id > 5580702) ORDER BY id ASC LIMIT 10; 
+----+-------------+-------+-------+---------------+---------+---------+------+---------+-------------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref | rows | Extra  | 
+----+-------------+-------+-------+---------------+---------+---------+------+---------+-------------+ 
| 1 | SIMPLE  | t  | range | PRIMARY  | PRIMARY | 4  | NULL | 1597518 | Using where | 
+----+-------------+-------+-------+---------------+---------+---------+------+---------+-------------+ 

现在,什么是真正奇怪的是,如果我强迫MySQL使用了“IS_ACTIVE人指数,查询再次发生瞬间!

+----+-------------+-------+-------+---------------+---------+---------+------+---------+-------------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref | rows | Extra  | 
+----+-------------+-------+-------+---------------+---------+---------+------+---------+-------------+ 
| 1 | SIMPLE  | t  | range | is_active  |is_active| 5  | NULL | 1866730 | Using where | 
+----+-------------+-------+-------+---------------+---------+---------+------+---------+-------------+ 

我只是不明白这种行为。在'is_active'索引中,行应按is_active排序,后跟id。我在查询中同时使用了'is_active'和'id'列,所以它似乎应该只需要在树上做几跳就可以找到ID,然后使用这些ID从表中检索标题。

发生了什么事?

编辑:更多信息关于我在做什么:

  • 查询缓存被禁用
  • 运行OPTIMIZE TABLE和ANALYZE TABLE没有效果
  • 6620372行已 'IS_ACTIVE' 设置为True。 874,714行将'is_active'设置为False。
  • 使用FORCE INDEX(is_active)再次加快查询速度。
  • MySQL版本54年1月5日
+2

您在基准测试之前清除缓存,对吧? – dfb

+0

同时确保表格统计数据是当前的,并且索引被重建。 (然而,这是在MySQL中完成的;-) – 2011-09-29 02:09:47

+0

如果您反转WHERE条件会发生什么? 'where(track.id> 5580702 and track.is_active = 1)' – EJP

回答

7

它看起来像MySQL正在做一个糟糕的决定如何使用索引。

从该查询计划中可以看出,它可以使用PRIMARY或is_active索引,并且选择了is_active以便首先通过track.is_active进行缩小。但是,它只使用索引的第一列(track.is_active)。那得到它3747543结果,然后必须被过滤和排序。

如果它已经选择了PRIMARY索引,它将能够使用索引将其缩小到1597518行,并且它们将按track.id的顺序检索,这不需要进一步排序。那会更快。

新的信息:

在您使用FORCE INDEX第三种情况下,MySQL的使用只能用第一列,它使用两列(见key_len)的IS_ACTIVE指数,但现在不是。因此,它现在能够通过is_active进行缩小,并使用相同的索引通过id进行排序和过滤,并且因为is_active是单个常量,所以ORDER BY由第二列满足(即,索引的单个分支中的行已经按排序顺序)。这似乎是比使用PRIMARY更好的结果 - 也许你首先想要的是,对吧?

我不知道为什么它不使用FORCE INDEX这个索引的两个列,除非查询之间以微妙的方式更改。如果没有,我会把它放到MySQL做出错误的决定。

+0

当然,如果你知道比MySQL更好,你总是可以使用[USE INDEX()](http://dev.mysql.com/doc/refman/5.5/en/index-hints.html)来建议它的索引应该更喜欢。你也可以尝试[ANALYZE TABLE](http://dev.mysql.com/doc/refman/5.5/en/analyze-table.html)给MySQL一个自己弄清楚的机会,有时这可能有效。 – thomasrutter

+0

如果我使用FORCE INDEX(is_active),则查询立即发生(请参阅最近的编辑)。有任何想法吗? – cwick

+0

我不确定 - 可能是某种缓存?也许为此添加EXPLAIN输出?你是否以相同的顺序获得相同的输出? – thomasrutter

1

我认为加速是由于您的WHERE子句。我假设它只是检索整个大表中的一小部分行。在小型子集上对is_active的检索数据进行表扫描比对大型索引文件进行筛选要快。遍历单列索引比遍历组合索引要快得多。

相关问题