MySQL排序的WHERE IN查询仍然使用文件排序而不是索引。

huangapple go评论60阅读模式
英文:

MySQL sorted WHERE IN query still uses filesort instead of index

问题

I am using using MySQL (InnoDB engine). I am running SQL query with WHERE IN statement which is also ordered.

我正在使用MySQL(InnoDB引擎)。我正在运行带有WHERE IN语句的SQL查询,该语句还被排序。

I decided to create the perfect index, so my query will not run slow. Surprisingly, after creating the perfect index MySQL is still using filesort when running my query.

我决定创建完美的索引,以便我的查询不会运行缓慢。令人惊讶的是,在创建完美的索引之后,MySQL在运行我的查询时仍然使用了文件排序。

My Query:

SELECT *
	FROM `events`
	WHERE
		`events`.`user_id` IN (118, 105, 126, 196, 338)
	ORDER BY
		`date` ASC,
		`planing_hour_id` ASC;

My perfect Index:

ALTER TABLE `events`
    ADD INDEX `my_perfect_index` (
        `user_id`,
        `date` ASC,
        `planing_hour_id` ASC
    );

Output of EXPLAIN:
MySQL排序的WHERE IN查询仍然使用文件排序而不是索引。

Problem:

MySQL still uses filesort even when there is perfect index available. I would expect only Using Index condition to be present in output of EXPLAIN query. Why is this not the case?

Disclaimer:

I checked similar topics and none of them has been useful:

英文:

I am using using MySQL (InnoDB engine). I am running SQL query with WHERE IN statement which is also ordered.

I decided to create the perfect index, so my query will not run slow. Surprisingly, after creating the perfect index MySQL is still using filesort when running my query.

My Query:

SELECT *
	FROM `events`
	WHERE
		`events`.`user_id` IN (118, 105, 126, 196, 338)
	ORDER BY
		`date` ASC,
		`planing_hour_id` ASC;

My perfect Index:

ALTER TABLE `events`
    ADD INDEX `my_perfect_index` (
        `user_id`,
        `date` ASC,
        `planing_hour_id` ASC
    );

Output of EXPLAIN:
MySQL排序的WHERE IN查询仍然使用文件排序而不是索引。

Problem:

MySQL still uses filesort even when there is perfect index available. I would expect only Using Index condition to be present in output of EXPLAIN query. Why is this not the case?

Disclaimer:

I checked similar topics and none of them has been useful:

答案1

得分: 1

以下是翻译好的内容:

如果MySQL读取行的自然顺序与您在ORDER BY中请求的顺序匹配,您可以消除“使用文件排序”。因此,不需要排序,因为它们已按正确顺序读取。

MySQL始终按索引顺序读取。无论它使用像您的这样的辅助索引,还是主键(也称为聚集索引),行都将按其存储在该索引中的顺序读取。因此,如果该顺序与您的ORDER BY不匹配,那么需要进行文件排序。

(注意,“文件排序”不一定意味着它将在磁盘上。如果结果中的行数很少,排序可以在RAM中相对快速地完成。)

那么为什么您的索引不被视为“完美”索引呢?

想象一下这个类比:

SELECT ... FROM TelephoneBook 
WHERE last_name IN ('Addams', 'Kirk', 'Smith') 
ORDER BY first_name;

结果:

last_name first_name
Addams Morticia
Kirk James
Smith Sarah Jane

这些按last_name的索引顺序读取。但它们不是按照查询中请求的first_name顺序隐式排序。查询需要一个阶段来对匹配的行集进行文件排序。

如果您以这种方式创建“完美”索引:

ALTER TABLE `TelephoneBook`
    ADD INDEX `my_perfect_index` (
        `first_name`,
        `last_name`
    );

查询可能会使用这个索引,并消除文件排序,因为它按first_name的顺序读取行。然而,这不能最有效地执行匹配last_name的行的搜索。

它最多可以做的是索引条件推送,这是告诉存储引擎不返回不匹配索引的行 - 但存储引擎必须检查所有索引来评估它们。这对于通过以last_name开头的索引读取不如。

最终,在必须使用范围条件进行查询(IN()符合范围条件)并进行不匹配范围条件列的排序的情况下,没有完美的索引

英文:

You can eliminate "Using filesort" if MySQL's natural order of reading the rows matches the order you request in your ORDER BY. Thus there is no sorting needed, because they are already read in the right order.

MySQL always reads in index order. Whether it uses a secondary index like yours, or the primary key (also called the clustered index), rows will be read in the order they are stored in that index. So if that order does not match your ORDER BY, then a filesort is required.

(Note "filesort" doesn't necessarily mean it will be on disk. If there are few rows in the result, the sorting can be done in RAM relatively quickly.)

So why doesn't your index count as a "perfect" index?

Think of this analogy:

SELECT ... FROM TelephoneBook 
WHERE last_name IN ('Addams', 'Kirk', 'Smith') 
ORDER BY first_name;

The result:

last_name first_name
Addams Morticia
Kirk James
Smith Sarah Jane

These are read in index order by last_name. But they are not implicitly in order by first_name, the order requested in the query. The query needs a phase to filesort the set of matching rows.

If you were to make the "perfect" index this way:

ALTER TABLE `TelephoneBook`
    ADD INDEX `my_perfect_index` (
        `first_name`,
        `last_name`
    );

The query might use this index, and eliminate the filesort, because it's reading the rows in order by first_name. However, this can't also do the search to find rows matching last_name most efficiently.

The best it can do is index condition pushdown, which is to tell the storage engine not to return rows that don't match the index — but the storage engine does have to examine all the indexes to evaluate them. This is not as good for optimization as reading from an index leading with last_name.

Ultimately, there is no perfect index in cases where the query must search with a range condition (IN() qualifies as a range condition), and also do a sort that doesn't match the column of the range condition.

答案2

得分: 1

如果您关心高性能,可以尝试将您的查询重写为:

select * from events where user_id = 118
union all select * from events where user_id = 105
union all select * from events where user_id = 116
union all select * from events where user_id = 196
union all select * from events where user_id = 338
order by date, planning_hour_id

这种形式有效地五次查找索引(尽管每次都会命中辅助和主要索引),然后将结果集联合在一起,最后对它们进行排序。

在这种情况下,简单的索引是有用的,因为排序不会使用索引,而需要物化。除非查询最终选择了数千行,否则没有问题。

create index i1 on events (user_id);
英文:

If your concern is high performance you can try rewriting your query as:

select * from events where user_id = 118
union all select * from events where user_id = 105
union all select * from events where user_id = 116
union all select * from events where user_id = 196
union all select * from events where user_id = 338
order by date, planning_hour_id

This form effectivelly seeks the index five times (albeit hitting the secondary and primary index each time), then unions the result sets, and finally sort them.

In this case a simple index is useful, since the sorting won't use the index and will require materialization. Nothing bad with it unless the query ends up selecting thousands of rows.

create index i1 on events (user_id);

huangapple
  • 本文由 发表于 2023年4月13日 16:47:49
  • 转载请务必保留本文链接:https://go.coder-hub.com/76003456.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定