为什么在MySQL中删除此索引会加速我的查询100x?

时间:2011-09-29 01:53:43

标签: mysql indexing

我有以下MySQL表(简化):

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无效
  • 6,620,372行将'is_active'设置为True。 874,714行的'is_active'设置为False。
  • 再次使用FORCE INDEX(is_active)加速查询。
  • MySQL版本5.1.54

3 个答案:

答案 0 :(得分:7)

看起来MySQL对如何使用索引做出了糟糕的决定。

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

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

新信息:

在使用FORCE INDEX的第三种情况下,MySQL正在使用is_active索引,但现在不是仅使用第一列,而是使用两列(请参阅key_len)。因此,它现在能够通过is_active进行缩小,并使用相同的索引对id进行排序和过滤,并且由于is_active是单个常量,因此第二列满足ORDER BY(即索引的单个分支中的行已经是按排序顺序)。这似乎比使用PRIMARY更好 - 甚至可能是你想要的,对吗?

我不知道为什么没有FORCE INDEX就不使用这个索引的两列,除非查询在两者之间以微妙的方式发生了变化。如果不是,我会把它归结为MySQL做出错误的决定。

答案 1 :(得分:1)

我认为加速是由你的where子句引起的。我假设它只检索整个大表中的一小部分行。对小型子集上的is_active执行检索数据的表扫描比通过大型索引文件进行过滤更快。遍历单个列索引要比遍历组合索引快得多。

答案 2 :(得分:0)

你可以尝试几件事: