MySQL:使用IN和ORDER BY时避免使用filesort

时间:2009-09-17 19:28:29

标签: sql mysql performance filesort

假设我有下表(我们称之为my_table):

CREATE TABLE `my_table` (
  `table_id` int(10) unsigned NOT NULL auto_increment,
  `my_field` int(10) unsigned NOT NULL default '0'
   PRIMARY KEY  (`table_id`),
   KEY `my_field` (`my_field`,`table_id`)
 ) ENGINE=MyISAM

my_table的主键是table_id(auto_increment),我还有my_fieldtable_id的密钥。

如果我测试此查询...

EXPLAIN SELECT * FROM my_table
WHERE my_field = 28
ORDER BY table_id DESC;

......我明白了:

id  select_type  table     type  possible_keys  key       key_len  ref    rows  Extra
--- -----------  --------  ----  -------------  --------  -------  -----  ----  -----
1   SIMPLE       my_table  ref   my_field       my_field  8        const  36

您可以看到它正在使用正确的密钥(my_field)。

但如果我试试这个......

EXPLAIN SELECT * FROM my_table
WHERE my_field IN (1, 28, 20)
ORDER BY table_id DESC;

......我明白了:

id  select_type  table     type  possible_keys  key     key_len  ref     rows  Extra
--- -----------  --------  ----  -------------  ------  -------  ------  ----  ---------------------------
1   SIMPLE       my_table  ALL   my_field       (NULL)  (NULL)   (NULL)  406   Using where; Using filesort

您可以看到它根本没有使用任何密钥,更糟糕的是,使用 filesort

即使我执行“FORCE INDEX (my_field)”,它仍会执行文件排序。

有没有办法避免使用filesort?

4 个答案:

答案 0 :(得分:13)

据我了解,MySQL无法使用索引对此查询进行排序。

MySQL只能使用索引,如果它恰好按照与查询相同的方式排序。假设(table_id,my_field)的记录是

(1,1), (2,28), (3,14), (4,20)

(my_field,table_id)上的索引将像这样存储

(1,1), (14,3), (20,4), (28,2)

从IN示例执行查询时(为简单起见,我们会说你的ORDER BY是ASCending),MySQL会找到

(1,1), (20,4), (28,2)

...按此顺序。无论如何,它都必须将它们分类为(1,1),(28,2),(20,4)。那是文件档案。这就是为什么MySQL只能在查询为ORDER BY my_fieldORDER BY my_field, table_id时使用该索引,因为索引已按此顺序排列。这也是为什么它不能 [目前,某些未来版本可能允许您按混合顺序对复合索引进行排序] 如果混合使用ASC和DESC,则使用索引。索引在ASC,ASC中排序,无论您以何种方式阅读它,它都将没有正确的顺序。

请注意“filesort”没有任何问题,它是正常执行查询的一部分。它实际上并不使用文件,应该非常快。

如果你必须排序成千上万的行,你可以通过使用一个小的派生表来获得更好的结果,特别是如果每​​一行都很大(很多字段,BLOB等......)

  SELECT t.*
    FROM (
          SELECT table_id FROM my_table WHERE my_field IN (1, 28, 20)
         ) tmp
    JOIN my_table t USING (table_id)
ORDER BY t.table_id DESC

您将为派生表交换文件排序。在某些情况下,它可以更高性能,而在其他情况下,稍微更少。 YMMV

答案 1 :(得分:1)

首先,我认为您的密钥在SQL中是倒退的。您是否希望主键为 table_id

这可能是你想要的:

CREATE TABLE `my_table` (
  `table_id` int(10) unsigned NOT NULL auto_increment,
  `my_field` int(10) unsigned NOT NULL default '0'
   PRIMARY KEY  (`table_id`),
   INDEX `my_index` (`my_field`)
 ) ENGINE=MyISAM

答案 2 :(得分:1)

您的查询有一个错误的“=”符号。像这样删除它:

EXPLAIN SELECT * FROM my_table
WHERE my_field IN (1, 28, 20)
ORDER BY table_id DESC;

答案 3 :(得分:1)

它不会使用密钥my_field,因为它是基于table_idmy_field的密钥。如果您仅根据my_field执行查询,则必须在my_field上独占一个索引。

在多个列上使用单个键并不意味着如果您按任何列搜索,则使用该键。仅当查询中的所有搜索列也在密钥中时,才使用特定密钥。