MySQL在使用ORDER BY时不使用索引(“使用filesort”)

时间:2009-04-07 12:11:36

标签: mysql performance optimization

由于在我的SQL代码中使用了“ORDER BY”语句,我遇到了一些非常重要的性能问题。

只要我没有在SQL中使用ORDER BY语句,一切都很好。但是,一旦我在SQL代码中引入ORDER BY:s,由于缺少正确的索引,一切都会显着减慢。人们会认为解决这个问题是微不足道的,但从论坛讨论等方面来看,这似乎是一个相当普遍的问题,我还没有看到这个问题的明确和简明的答案。

问题:鉴于下表......

CREATE TABLE values_table (
  id int(11) NOT NULL auto_increment,
  ...
  value1 int(10) unsigned NOT NULL default '0',
  value2 int(11) NOT NULL default '0',
  PRIMARY KEY  (id),
  KEY value1 (value1),
  KEY value2 (value2),
) ENGINE=MyISAM AUTO_INCREMENT=2364641 DEFAULT CHARSET=utf8;

...如何在对 value1 的值进行排序时,在查询表时使用的索引??

目前,在不使用ORDER BY子句时,提取正常。

请参阅以下EXPLAIN QUERY输出:

OK, when NOT using ORDER BY:

EXPLAIN select ... from values_table this_ where this_.value1 between 12345678 and 12349999 limit 10;

+----+-------------+-------+-------+---------------+----------+---------+------+------+-------------+
| id | select_type | table | type  | possible_keys | key      | key_len | ref  | rows | Extra       |
+----+-------------+-------+-------+---------------+----------+---------+------+------+-------------+
|  1 | SIMPLE      | this_ | range | value1        | value1   | 4       | NULL | 3303 | Using where |
+----+-------------+-------+-------+---------------+----------+---------+------+------+-------------+
However, when using ORDER BY I get "Using filesort":

EXPLAIN select ... from values_table this_ where this_.value1 between 12345678 and 12349999 order by this_.value2 asc limit 10;

+----+-------------+-------+-------+---------------+----------+---------+------+------+-----------------------------+
| id | select_type | table | type  | possible_keys | key      | key_len | ref  | rows | Extra                       |
+----+-------------+-------+-------+---------------+----------+---------+------+------+-----------------------------+
|  1 | SIMPLE      | this_ | range | value1        | value1   | 4       | NULL | 3303 | Using where; Using filesort |
+----+-------------+-------+-------+---------------+----------+---------+------+------+-----------------------------+

有关表格内容的一些其他信息:

SELECT MIN(value1), MAX(value1) FROM values_table;
+---------------+---------------+
| MIN(value1)   | MAX(value2)   |
+---------------+---------------+
|             0 |    4294967295 |
+---------------+---------------+

...

SELECT MIN(value2), MAX(value2) FROM values_table;
+---------------+---------------+
| MIN(value2)   | MAX(value2)   |
+---------------+---------------+
|             1 |        953359 |
+---------------+---------------+

如果需要任何进一步的信息来回答这个问题,请告诉我。

提前多多感谢!

更新#1:添加新的复合索引( ALTER TABLE values_table ADD INDEX(value1,value2); )无法解决问题。添加这样的索引后,你仍会得到“使用filesort”。

更新#2:我在我的问题中没有提到的一个约束是我宁愿改变表的结构(比如添加索引等)而不是改变使用的SQL查询。 SQL查询是使用Hibernate自动生成的,所以要考虑那些或多或少固定的。

2 个答案:

答案 0 :(得分:19)

在这种情况下,您不能使用索引,因为您使用RANGE过滤条件。

如果你使用类似的东西:

SELECT  *
FROM    values_table this_
WHERE   this_.value1 = @value
ORDER BY
        value2
LIMIT 10

,然后在(VALUE1, VALUE2)上创建复合索引将用于过滤和排序。

但是你使用远程条件,这就是你无论如何都需要进行排序的原因。

您的复合索引将如下所示:

value1 value2
-----  ------
1      10
1      20
1      30
1      40
1      50
1      60
2      10
2      20
2      30
3      10
3      20
3      30
3      40

,如果您在1中选择2value1,则仍然无法获得value2的整个有序集。

如果value2上的索引不是很有选择性(即表中的DISTINCT value2不多),您可以尝试:

CREATE INDEX ix_table_value2_value1 ON mytable (value2, value1)

/* Note the order, it's important */    

SELECT  *
FROM    (
        SELECT  DISTINCT value2
        FROM    mytable
        ORDER BY
                value2
        ) q,
        mytable m
WHERE   m.value2 >= q.value2
        AND m.value2 <= q.value2
        AND m.value1 BETWEEN 13123123 AND 123123123

这称为SKIP SCAN访问方法。 MySQL不直接支持它,但可以像这样模拟它。

在这种情况下将使用RANGE访问权限,但除非DISTINCT value2包含少于约1%行,否则您可能无法获得任何性能优势。

注意使用:

m.value2 >= q.value2
AND m.value2 <= q.value2

而不是

m.value2 = q.value2

这使MySQL对每个循环执行RANGE检查。

答案 1 :(得分:0)

在我看来,你有两个完全独立的键,一个用于value1,一个用于value2。

因此,当您使用value1键进行检索时,记录不一定按value2的顺序返回,因此必须对它们进行排序。这仍然比全表扫描更好,因为您只对满足“where value1”子句的记录进行排序。

我认为(如果在MySQL中这是可能的),(value1,value2)上的复合键可以解决这个问题。

尝试:

CREATE TABLE values_table (
    id int(11) NOT NULL auto_increment,
    ...
    value1 int(10) unsigned NOT NULL default '0',
    value2 int(11) NOT NULL default '0',
    PRIMARY KEY  (id),
    KEY value1 (value1),
    KEY value1and2 (value1,value2),
) ENGINE=MyISAM AUTO_INCREMENT=2364641 DEFAULT CHARSET=utf8;

(或等效的ALTER TABLE),假设MySQL中的复合键是正确的语法。

在我知道的所有数据库中(我必须承认MySQL不是其中之一),这会导致数据库引擎选择value1and2键来检索行,并且它们已经在value2-within-value1中排序订单,所以不需要文件排序。

如果需要,您仍然可以保留value2键。