【问题标题】:MySQL not using indexes ("Using filesort") when using ORDER BYMySQL 在使用 ORDER BY 时不使用索引(“使用文件排序”)
【发布时间】:2010-10-18 01:02:54
【问题描述】:

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

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

问题:给定下表...

创建表 values_table ( id int(11) NOT NULL auto_increment, ... value1 int(10) unsigned NOT NULL default '0', value2 int(11) NOT NULL 默认 '0', 主键(id), 键值1(值1), 键值2(值2), ) 引擎=MyISAM AUTO_INCREMENT=2364641 默认字符集=utf8;

...如何创建索引,以便在查询表以获取 value1 范围同时对 value2 的值进行排序时使用?

目前,当不使用 ORDER BY 子句时,获取是可以的。

查看以下 EXPLAIN QUERY 输出:

好的,当不使用 ORDER BY 时: 解释 select ... from values_table this_ where this_.value1 介于 12345678 和 12349999 之间,限制为 10; +----+-------------+--------+-------+-------------- -+----------+---------+------+------+------------- + |编号 |选择类型 |表|类型 |可能的键 |关键 | key_len |参考 |行 |额外 | +----+-------------+--------+-------+-------------- -+----------+---------+------+------+------------- + | 1 |简单 |这个_ |范围 |值1 |值1 | 4 |空 | 3303 |使用位置 | +----+-------------+--------+-------+-------------- -+----------+---------+------+------+------------- + 但是,当使用 ORDER BY 时,我得到“使用文件排序”: 解释 select ... from values_table this_ where this_.value1 在 12345678 和 12349999 之间 order by this_.value2 asc limit 10; +----+-------------+--------+-------+-------------- -+----------+---------+------+------+------------- ----------------+ |编号 |选择类型 |表|类型 |可能的键 |关键 | key_len |参考 |行 |额外 | +----+-------------+--------+-------+-------------- -+----------+---------+------+------+------------- ----------------+ | 1 |简单 |这个_ |范围 |值1 |值1 | 4 |空 | 3303 |使用哪里;使用文件排序 | +----+-------------+--------+-------+-------------- -+----------+---------+------+------+------------- ----------------+

关于表格内容的一些附加信息:

从值表中选择最小值(值 1)、最大值(值 1); +---------------+---------------+ |最小值(值 1)|最大值(值2)| +---------------+---------------+ | 0 | 4294967295 | +---------------+---------------+ ... 从值表中选择最小值(值 2)、最大值(值 2); +---------------+---------------+ |最小值(值 2)|最大值(值2)| +---------------+---------------+ | 1 | 953359 | +---------------+---------------+

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

提前非常感谢!

更新 #1: 添加新的复合索引 (ALTER TABLE values_table ADD INDEX (value1, value2);) 并不能解决问题。添加这样的索引后,您仍然会得到“使用文件排序”。

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

【问题讨论】:

  • 我假设您在更新中的意思是 value1,value2,是吗?
  • 没关系,因为@Quassnoi 对 value1 范围的解释,这无论如何都行不通。它适用于 value1 的单个值,但我没有很好地阅读这个问题。祝你好运。
  • 您是直接在查询中使用字段还是使用函数?像时间戳字段和 WEEK(timestamp)。
  • heikogerlach:字段按原样使用。没有使用任何函数。
  • +1 写得很好的问题

标签: mysql performance optimization


【解决方案1】:

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

如果你会使用类似的东西:

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

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

但是您使用范围条件,这就是为什么您无论如何都需要执行排序。

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

值1 值2 ----- ------ 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

,如果您在value1 中选择12,您仍然不会得到完整的排序集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 检查。

【讨论】:

  • 感谢您的全面回答。假设我无法更改使用的 SQL 查询(这些是由 Hibernate 自动生成的),您是否认为这不可能解决(通过添加更好的索引)?
  • 另一个问题:如果范围查询是问题所在,为什么不使用 ORDER BY 时一切似乎都正常?对不起,如果我错过了这个细节。
  • 因为索引还是用来过滤的,如你第二个EXPLAIN所示。在您的查询中,不使用索引的是 ORDER BY,而不是 WHERE。
  • 我不认为你可以在不重写查询的情况下提高性能。您甚至无法创建视图,因为它会使用 MySQL 不允许在视图中使用的嵌套子查询。
  • 我对Hibernate没有深入的了解,但我认为可以使用自定义SQL:hibernate.org/hib_docs/reference/en/html/querysql.html。再说一次,这只是我在谷歌上搜索的东西,我没有在实践中尝试过。
【解决方案2】:

在我看来,您有两个完全独立的键,一个用于 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 键。

【讨论】:

  • 您好,感谢您的快速回复。我已经尝试过您建议的解决方案,但不幸的是它不起作用。我已对我的问题进行了澄清。
  • 没有问题,看起来@Quassnoi 对 MySQL 有更多的了解,所以我会留给你。他对为什么需要对范围 value1 进行排序的解释是我没有从问题中得到的——DB2 会有类似的问题。标记为 community-wiki,因此没有人会犯同样的错误。
  • 有某种 SO 错误阻止我标记问题 community-wiki。
  • 很奇怪,你不能只是编辑来制作社区维基,你还必须改变答案——这是新功能。
猜你喜欢
  • 1970-01-01
  • 2011-09-29
  • 2012-03-27
  • 1970-01-01
  • 2010-11-29
  • 2015-06-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多