12

我的 mySQL 表的排序不使用索引,我不知道为什么。

我有:

CREATE TABLE IF NOT EXISTS `test` (
  `a` int(11) NOT NULL,
  `b` int(11) NOT NULL,
  KEY `kk` (`a`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

还有这个:

EXPLAIN SELECT * 
FROM test
ORDER BY a

还有这个

EXPLAIN SELECT * 
FROM test
USE INDEX ( kk ) 
ORDER BY a

给了我这个:

id select_type table type possible_keys key  key_len ref  rows  Extra
1  SIMPLE      test  ALL  NULL          NULL NULL    NULL 10009 Using filesort

我不想看到这个文件排序,并使用键 kk 对我的表进行排序。我究竟做错了什么?


谢谢你的帖子,他们回答了我的问题!但是,现在我不明白“表扫描”和“文件排序”是什么意思?即使我选择表的所有字段和所有行,通过在 O(n) 中遍历该列的索引的内部树(然后在表中查找)按一列对该表进行排序不是更快吗在 O(1) 中为每一行归档请求的额外列 => 索引文件将每一行的物理位置存储在表文件中,或者?),而不是通过快速排序在 O(n * log n) 中进行排序(可能)在表文件中随机存储行,而不触及索引?我想我对索引如何在 mySQL 中工作的理解是错误的。

4

3 回答 3

18
  1. 您正在选择所有行
  2. 您正在选择所有列

按照我上面所说的 - mysql 估计使用完整扫描更有效。

要使用索引获取它,您需要添加一些WHERE将其限制为返回的合理行数(比如 50)

于 2013-07-18T00:26:17.437 回答
6

@zerkms 是正确的,通过读取表中的所有行,MySQL 决定无论如何都必须读取表的大部分内容,因此也不需要读取索引。如果您选择表的子集,优化器会改变行为。

例如,我创建了一个像您这样的表并用 16384 行填充,其中随机整数介于 0 和 1000000 之间。然后我尝试 EXPLAIN 对表的不同子集,首先是表的 15%,然后是 17%,然后是 19%。

mysql> EXPLAIN SELECT *  FROM test where a < 150000 ORDER BY a;
+----+-------------+-------+-------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type  | possible_keys | key  | key_len | ref  | rows | Extra       |
+----+-------------+-------+-------+---------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | test  | range | kk            | kk   | 5       | NULL | 2272 | Using where |
+----+-------------+-------+-------+---------------+------+---------+------+------+-------------+

mysql> EXPLAIN SELECT *  FROM test where a < 170000 ORDER BY a;
+----+-------------+-------+-------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type  | possible_keys | key  | key_len | ref  | rows | Extra       |
+----+-------------+-------+-------+---------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | test  | range | kk            | kk   | 5       | NULL | 2560 | Using where |
+----+-------------+-------+-------+---------------+------+---------+------+------+-------------+

mysql> EXPLAIN SELECT *  FROM test where a < 190000 ORDER BY a;
+----+-------------+-------+------+---------------+------+---------+------+-------+-----------------------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows  | Extra                       |
+----+-------------+-------+------+---------------+------+---------+------+-------+-----------------------------+
|  1 | SIMPLE      | test  | ALL  | kk            | NULL | NULL    | NULL | 16384 | Using where; Using filesort |
+----+-------------+-------+------+---------------+------+---------+------+-------+-----------------------------+

您还可以通过减少列来说服它使用索引,直到您只选择索引的列。它将决定单独读取索引,而不是触摸表格。如果需要,您可以定义带有额外列的索引,即使搜索或排序不需要这些列。

mysql> ALTER TABLE test ADD KEY kk2 (a,b);
mysql> EXPLAIN SELECT a,b FROM test ORDER BY a;
+----+-------------+-------+-------+---------------+------+---------+------+-------+-------------+
| id | select_type | table | type  | possible_keys | key  | key_len | ref  | rows  | Extra       |
+----+-------------+-------+-------+---------------+------+---------+------+-------+-------------+
|  1 | SIMPLE      | test  | index | NULL          | kk2  | 10      | NULL | 16384 | Using index |
+----+-------------+-------+-------+---------------+------+---------+------+-------+-------------+
于 2013-07-18T00:36:16.763 回答
1

由于您没有 WHERE 子句,它将执行文件排序(表扫描),除非您选择的唯一项目来自索引。此查询将使用索引。看到这个SQL Fiddle

EXPLAIN SELECT a FROM test ORDER BY a

但是,如果您选择不在索引中的列(* 或 b),它将执行文件扫描。添加带有覆盖索引的 where 子句或更改您选择的列。

于 2013-07-18T00:31:56.750 回答