【问题标题】:MySQL not using index for ORDER BYMySQL 不使用 ORDER BY 的索引
【发布时间】:2026-02-06 13:55:01
【问题描述】:

我有一个名为“test”的简单 MySQL 表,有两列:

  1. 名为“id”的自动递增 int 列
  2. 名为“textcol”的 Varchar(3000) 列

我根据“textcol”列在表中创建索引。但是,ORDER BY 查询似乎没有使用索引,即在 textcol 上使用 ORDER BY 的简单查询上的 EXPLAIN 语句在其输出的 Key 列中显示 NULL,并且还使用文件排序。

任何用于进行更改以帮助使用 ORDER by 查询的索引的指针都会对我有用。

“mysql --version”命令给出的MySQL版本:

mysql Ver 14.14 Distrib 5.1.58,适用于使用 readline 6.2 的 debian-linux-gnu (x86_64)

mysql> CREATE TABLE test (id INTEGER NOT NULL AUTO_INCREMENT, PRIMARY KEY(id), textcol VARCHAR(3000));
Query OK, 0 rows affected (0.05 sec)

mysql> DESCRIBE test;
+---------+---------------+------+-----+---------+----------------+
| Field   | Type          | Null | Key | Default | Extra          |
+---------+---------------+------+-----+---------+----------------+
| id      | int(11)       | NO   | PRI | NULL    | auto_increment |
| textcol | varchar(3000) | YES  |     | NULL    |                |
+---------+---------------+------+-----+---------+----------------+
2 rows in set (0.00 sec)

mysql> CREATE INDEX textcolindex ON test (textcol);
Query OK, 0 rows affected, 2 warnings (0.06 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> SHOW INDEX FROM test;
+-------+------------+--------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| Table | Non_unique | Key_name     | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+-------+------------+--------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| test  |          0 | PRIMARY      |            1 | id          | A         |           0 |     NULL | NULL   |      | BTREE      |         |
| test  |          1 | textcolindex |            1 | textcol     | A         |        NULL |     1000 | NULL   | YES  | BTREE      |         |
+-------+------------+--------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
2 rows in set (0.00 sec)

mysql> INSERT INTO test (textcol) VALUES ('test1');
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO test (textcol) VALUES ('test2');
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO test (textcol) VALUES ('test3');
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO test (textcol) VALUES ('test4');
Query OK, 1 row affected (0.00 sec)


mysql> EXPLAIN SELECT * FROM test ORDER BY textcol;
+----+-------------+-------+------+---------------+------+---------+------+------+----------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra          |
+----+-------------+-------+------+---------------+------+---------+------+------+----------------+
|  1 | SIMPLE      | test  | ALL  | NULL          | NULL | NULL    | NULL |    4 | Using filesort |
+----+-------------+-------+------+---------------+------+---------+------+------+----------------+
1 row in set (0.00 sec)

mysql> EXPLAIN SELECT * FROM test ORDER BY id;
+----+-------------+-------+------+---------------+------+---------+------+------+----------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra          |
+----+-------------+-------+------+---------------+------+---------+------+------+----------------+
|  1 | SIMPLE      | test  | ALL  | NULL          | NULL | NULL    | NULL |    4 | Using filesort |
+----+-------------+-------+------+---------------+------+---------+------+------+----------------+
1 row in set (0.00 sec)

【问题讨论】:

  • 您是否继续在表格中看到大量数据? Explain 不会告诉您它将始终如何处理此查询,而只会告诉您此时它将如何处理此查询。
  • 我将 textcol 列的大小更改为 10 个字节并插入了 30000 个新行。 SELECT * 中的 ORDER BY 仍然不使用索引。但是,我刚刚注意到 SELECT COUNT(*) 语句使用了索引。知道为什么会这样吗?

标签: mysql sql database indexing


【解决方案1】:

由于它必须加载整个表来回答查询并且对 4 个元素进行排序很便宜,因此查询优化器可能只是避免接触索引。更大的桌子还会发生这种情况吗?

请注意,varchar(3000) 列不能作为覆盖索引,因为 MySQL 不会在索引中包含超过 varchar 的前 768 个左右字节。

如果您希望查询仅读取索引,则索引必须包含您 SELECTing 的每一列。在 innodb 上,一旦你使 textcol 足够小,它应该开始为你的两列表工作;在 MyISAM 上,您需要自己包含主键列,例如 CREATE INDEX textcolindex ON test (textcol,id);

【讨论】:

  • 我将 textcol 列的大小更改为 10 个字节并插入了 30000 个新行。 SELECT * 中的 ORDER BY 仍然不使用索引。但是,我刚刚注意到 SELECT COUNT(*) 语句使用了索引。知道为什么会这样吗?
  • 澄清一下,MyISAM 的索引限制为 1000 字节,InnoDB 的索引限制为 767,远低于 3000 varchar 字段。实际上,MySQL 将使用一个索引前缀,它可用于帮助查找(例如 WHERE 子句),但它不能用于排序,因为它不完整:dev.mysql.com/doc/refman/5.0/en/order-by-optimization.html
  • @Kowshik,如果没有“覆盖”索引,MySQL 将不得不进行 30,000 次搜索才能利用该索引。文件排序仍然比 30,000 次查找更容易。请改用SELECT textcol ORDER BY textcol
  • @MarcusAdams:我明白了。谢谢(你的)信息。有什么方法可以加快:“SELECT * FROM TABLE ORDER BY X”查询?
  • @MarcusAdams:是的,SELECT textcol ORDER BY textcol 使用索引!此外,如果我创建一个包含所有列的不同索引,则 SELECT * 语句将使用该索引。 :)
【解决方案2】:

一些关于 ORDER BY 优化的有用文章:

http://www.mysqlperformanceblog.com/2006/09/01/order-by-limit-performance-optimization/

http://opsmonkey.blogspot.co.uk/2009/03/mysql-query-optimization-for-order-by.html

正如在很大程度上讨论的那样,将 varchar 保持在 767 并通过以下方式为订单添加一个键:

CREATE TABLE test (
id INTEGER NOT NULL AUTO_INCREMENT,
textcol VARCHAR(767),
PRIMARY KEY(id),
KEY orderby (`textcol`)
);

为避免filesorts 添加额外的“WHERE”参数,请使用多列索引扩展“orderby”索引键:

CREATE TABLE test (
id INTEGER NOT NULL AUTO_INCREMENT, 
tom INT(11) NOT NULL DEFAULT 0,
gerry INT(11) NOT NULL DEFAULT 0,
textcol VARCHAR(767),
PRIMARY KEY(id), 
KEY orderby (`tom`,`gerry`, `textcol`)
);

还有:

INSERT INTO test (tom, gerry, textcol) VALUES (1,2,'test4');
INSERT INTO test (tom, gerry, textcol) VALUES (1,2,'test2');
EXPLAIN SELECT id, textcol FROM test WHERE tom = 1 AND gerry =2 ORDER BY textcol;

额外:'使用where;使用索引'

【讨论】:

  • 感谢您的链接。虽然老了,但他们很有帮助
【解决方案3】:

我遇到了同样的问题。 MySQL是愚蠢的。仅供参考:我有一个包含超过 500,000,000 条记录的表。我想:

select * from table order by tid limit 10000000, 10;

tid是表中的主键,被mysql自动索引。

这花了很长时间,我取消了查询。然后我让mysql“解释”查询并认识到它不会使用索引作为主键。在从 mysql 阅读了许多文档后,我试图通过“USE INDEX(...)”强制 mysql 使用索引,并且 dis 也不起作用。然后我发现 mysql 似乎总是将 where 子句与 order by 子句相关联。因此,我尝试使用触及索引的条件扩展 where 子句。我最终得到:

select * from table use index (PRIMARY) where tid > 0 order by tid limit 10000000, 10;

其中 tid 是表中的主键,是一个从 1 开始的自增值。

这在我让 mysql 向我解释查询之后起作用。瞧:查询只用了 4 秒。

【讨论】:

  • 感谢您发布此信息。这可能有一天会派上用场