【问题标题】:MySQL: Avoid filesort when using IN and ORDER BYMySQL:使用 IN 和 ORDER BY 时避免文件排序
【发布时间】:2010-11-29 06:41:26
【问题描述】:

假设我有下表(我们称之为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 简单的 my_table 参考 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 使用 where;使用文件排序

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

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

有什么办法可以避免文件排序?

【问题讨论】:

  • 更新了我的答案。派生表可能会对您有所帮助。

标签: sql mysql performance filesort


【解决方案1】:

据我了解,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 排序,无论您以哪种方式阅读它,它都不会按正确的顺序排列。

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

如果您必须对数千行进行排序,使用小型派生表可能会获得更好的结果,尤其是当每一行都非常大时(很多字段、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

【讨论】:

  • 好吧,文件排序实际上在对大量记录进行排序时是个问题。遗憾的是 MySQL 不能使用索引来进行这种排序。
  • 很好的答案,但我不同意文件排序不是@Boro 所说的问题。如果您需要对许多行进行排序,这将不会很快。如果您需要对 10 行进行排序,则无需浪费时间进行优化。
【解决方案2】:

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

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

【讨论】:

    【解决方案3】:

    首先,我认为您的键在您的 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
    

    【讨论】:

    • 我的错误。实际的主键是 table_id。此表是我正在使用的实际表的精简版。
    【解决方案4】:

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

    在多个列上拥有一个键并不意味着如果您按任何列进行搜索,就会使用该键。仅当查询中的所有搜索列也在该键中时才使用特定键。

    【讨论】:

    • 我刚刚编辑了我的问题。该查询将 my_field 用于 WHERE 子句,将 table_id 用于 ORDER BY 子句,它们是 my_field 索引中包含的两列,其精确顺序是 MySQL 友好的:)
    猜你喜欢
    • 1970-01-01
    • 2015-08-05
    • 1970-01-01
    • 1970-01-01
    • 2010-11-22
    • 2019-11-20
    • 2010-10-18
    相关资源
    最近更新 更多