2014-05-25 249 views
1

我已经阅读了几个关于此问题的其他堆栈溢出问题,但它仍然没有意义。MySQL没有使用索引来排序

我与sakila的世界测试数据库进行试验,这是我的表定义:

CREATE TABLE `City` (
    `ID` int(11) NOT NULL AUTO_INCREMENT, 
    `Name` char(35) NOT NULL DEFAULT '', 
    `CountryCode` char(3) NOT NULL DEFAULT '', 
    `District` char(20) NOT NULL DEFAULT '', 
    `Population` int(11) NOT NULL DEFAULT '0', 
    PRIMARY KEY (`ID`), 
    KEY `CountryCode` (`CountryCode`), 
    KEY `city_name` (`Name`), 
    CONSTRAINT `city_ibfk_1` FOREIGN KEY (`CountryCode`) REFERENCES `Country` (`Code`) 
) ENGINE=InnoDB AUTO_INCREMENT=4080 DEFAULT CHARSET=latin1 

这里是我的指标:

mysql> show index from City; 
+-------+------------+-------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ 
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | 
+-------+------------+-------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ 
| City |   0 | PRIMARY  |   1 | ID   | A   |  4188 |  NULL | NULL |  | BTREE  |   |    | 
| City |   1 | CountryCode |   1 | CountryCode | A   |   465 |  NULL | NULL |  | BTREE  |   |    | 
| City |   1 | city_name |   1 | Name  | A   |  4188 |  NULL | NULL |  | BTREE  |   |    | 
+-------+------------+-------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ 

我试图理解为什么MySQL是不使用这里对结果进行排序的索引:

mysql> explain select * from City order by Name asc; 
+----+-------------+-------+------+---------------+------+---------+------+------+----------------+ 
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra   | 
+----+-------------+-------+------+---------------+------+---------+------+------+----------------+ 
| 1 | SIMPLE  | City | ALL | NULL   | NULL | NULL | NULL | 4188 | Using filesort | 
+----+-------------+-------+------+---------------+------+---------+------+------+----------------+ 

我不明白为什么MySQL在这个文件夹中做文件情况下,很明显城市名称上的索引已经排序。

我看了一些其他的问题,人们在他们的索引中使用前缀,这是禁止MySQL使用该索引进行排序。当我在名称上创建该索引时,我没有使用前缀。

其他人也期待在Extra列中看到“使用索引”。我的理解是,这意味着索引'覆盖'了查询,这意味着实际的表不需要被读取,因为索引具有所有的值。所以我不希望在额外列中看到这一点,因为索引仅在名称上,还有其他列。

我觉得这个术语“使用索引”有点让人误解,MySQL可以使用索引来过滤结果,但仍然需要读取表格。在这种情况下,“使用索引”不会成为Extra列的一部分。太误导了。

有人请向我解释为什么MySQL仍然在使用该查询的文件?如果你想知道的话,有4079行。

另外,是否有任何明确的方法知道MySQL使用索引来排序结果?

+0

优化器看到您将读取表中的所有记录,因此它倾向于从PRIMARY索引读取而不是从辅助city_name读取。因此MySQL必须做额外的排序。 – akuzminsky

回答

0

这是您的查询:

select * 
from City 
order by Name asc; 

有两个部分来此查询。一部分是以正确的顺序获取名称值。另一部分是获取所有其他列。 MySQL必须比较这两个操作的成本。

查询有两种可能的路径。首先是采取所有列并按名称排序。然后只是返回结果。这是filesort方法。第二个是按顺序读取索引,然后在数据表中逐一查找行。

MySQL已经决定第一种方法更快。如果你只有一行,这显然是真的(为什么只读一行索引)?我的猜测是你在表格中的数据非常少。随着您添加更多数据,指数的使用将会更加有益。

作为一个说明,这个查询通常应该使用索引方法:

select Name 
from City 
order by Name asc; 
+0

MySQL需要多少行才能决定使用第二种方法更好?我已经有超过4000行的表格。所以你在说排序4000行花费的时间少于从索引中获取行的时间。 – msknapp

+0

考虑使用某种优化的排序对任何一组4000个密钥进行排序。时间可以忽略不计。从磁盘传输更为昂贵(考虑您需要同时传输索引结构和表行)。然而,没有固定的数字,这一切都取决于基于统计和设计的启发式。 4000行是绝对微小的行数,所有事情都考虑在内。 – SystemFun

+0

@msknapp。 。 。 4,000似乎足以让MySQL使用索引。问题:为什么你使用'char()'值而不是'varchar()'?固定长度记录较大,可能会抛弃优化决策。 –

0

通常,当你不决定过滤结果(即没有where子句)时,RDBMS将决定使用排序/过滤器的方法(而不是索引),因为你上面提到的原因。您试图返回与表格中所有行相关的所有信息,只是为了返回索引列以外的数据而无需使用索引就可以更高效地完成查找和传输操作一旦在索引中找到密钥,即可对该表进行操作。

换句话说,索引建立在您选择的字段上,但不包含任何有关该表的其他相关信息......因此,它必须引用回表的真实位置检索你请求的附加元数据,这种简单的排序记录效率较低(假设你有这么小的数据集)。这是因为在您的(名称)列上对一个小数据集进行排序比根据索引检索值更快,并且将它们排序。

然而,从我提到的确切原因,从任意大的表中检索大容量的宽记录时,通常不会使用索引。你可以给出计划者的提示,这将强制它使用索引来验证我在这里提到的内容......你也可以通过一些大的因素来增加你的数据集,然后尝试选择一小部分来测试我的理论。