【问题标题】:MySQL not using Index to SortMySQL 不使用索引进行排序
【发布时间】:2014-05-25 03:27:01
【问题描述】:

我现在已经阅读了其他几个关于此的堆栈溢出问题,但仍然没有意义。

我正在尝试使用 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 列中看到“使用索引”。我的理解是这意味着索引“覆盖”了查询,这意味着不需要读取实际表,因为索引具有所有值。所以我不希望在 Extra 列中看到这一点,因为索引仅在名称上,并且还有其他列。

我觉得“使用索引”这个词有点误导,MySQL 可以使用索引来过滤结果,但仍然必须读取表。在这种情况下,“使用索引”不会是 Extra 列的一部分。太误导人了。

有人可以向我解释一下为什么 MySQL 仍在为该查询使用文件排序吗?如果你想知道的话,有 4079 行。

另外,有没有什么明确的方法可以知道 MySQL 使用索引对结果进行排序?

【问题讨论】:

  • 优化器看到你要从表中读取所有记录,所以它更喜欢从 PRIMARY 索引而不是二级 city_name 读取。因此 MySQL 必须进行额外的排序。

标签: mysql indexing


【解决方案1】:

这是您的查询:

select *
from City 
order by Name asc;

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

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

MySQL 决定第一种方法更快。如果您只有一行,这显然是正确的(当您可以读取行时,为什么要读取索引行?)。我的猜测是表中的数据很少。随着您添加更多数据,索引的使用将更加有益。

注意,这个查询一般应该使用索引方法:

select Name
from City 
order by Name asc;

【讨论】:

  • MySQL 需要多少行才能决定使用第二种方法更好?我的表中已经有超过 4000 行。所以你是说排序 4000 行比从索引中获取行花费的时间更少。
  • 考虑用一些优化的排序方式对任何 4000 个键的集合进行排序。时间可以忽略不计。从磁盘传输的成本要高得多(假设您需要同时传输索引结构和表行)。然而,没有固定的数字,这完全取决于基于统计和设计的启发式方法。考虑到所有因素,4000 行绝对是一个很小的行数。
  • @msknapp 。 . . 4,000 似乎足以让 MySQL 使用索引。问题:为什么你使用char() 值而不是varchar()?固定长度的记录较大,可能会影响优化决策。
  • @Gordon,就像我在问题中说的,我从 sakila 测试数据库中导入了数据,不是我自己做的。
【解决方案2】:

通常,当您不决定过滤结果(即没有 where 子句)时,RDBMS 将出于您上面提到的原因决定使用排序/过滤方法(而不是索引)。您试图返回与表中所有行有关的所有信息,这在不使用索引的情况下更有效地完成,因为为了返回索引列之外的数据,必须进行查找和传输一旦在索引中找到键,就针对表。

换句话说,索引建立在您选择的字段上,但不包含有关表的任何其他相关信息...因此,它必须参考表的真实位置才能检索您请求的其他元数据,这比简单地对记录进行排序效率低(假设您拥有如此小的数据集)。这样做的原因是,在 CPU 方面,对 (name) 列上的小数据集进行排序比根据索引检索值然后对它们进行排序更快。

但是,出于我提到的确切原因,从任意大的表中检索大量宽记录时,通常不使用索引。您可以给规划器提示,迫使它使用索引来验证我在这里提到的内容......您还可以将您的数据集扩大一些较大的因素,然后尝试选择其中的一小部分来测试我的理论。

【讨论】:

    猜你喜欢
    • 2014-07-29
    • 2011-10-15
    • 2018-09-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多