MySQL执行计划之Using filesort

一、介绍

  Using filesort是MySQL执行计划Extra字段中的一个重要值,表示查询出所有数据再进行排序。此排序方式为文件排序,没有走索引排序using index.
  一般来说,执行计划中如果Extra字段中值为Using filesort时,那么type字段(查询类型)一般为indexALL。(两者都是查询所有数据,index与ALL区别为index类型只遍历索引树。通常比ALL快,因为索引文件通常比数据文件小。Index与ALL虽然都是读全表,但index是从索引中读取,而ALL是从硬盘读取)

type字段的结果值,从好到坏依次是:system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL
一般来说,好的sql查询至少达到range级别,最好能达到ref

二、测试准备

1. 表结构
CREATE TABLE `test_filesort` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `key1` varchar(255) DEFAULT NULL,
  `key2` varchar(255) DEFAULT NULL,
  `key3` varchar(255) DEFAULT NULL,
  `key4` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_key123` (`key1`,`key2`,`key3`)
) 

索引为key1,key2,key3

2. 插入数据
insert into test_filesort (key1,key2,key3,key4)values ('001','a01','b01','c01');
insert into test_filesort (key1,key2,key3,key4)values ('001','a02','b02','c02');
insert into test_filesort (key1,key2,key3,key4)values ('001','a03','b03','c03');
insert into test_filesort (key1,key2,key3,key4)values ('001','a04','b04','c04');
insert into test_filesort (key1,key2,key3,key4)values ('002','a05','b05','c05');
insert into test_filesort (key1,key2,key3,key4)values ('002','a06','b06','c06');
insert into test_filesort (key1,key2,key3,key4)values ('002','a07','b07','c07');
insert into test_filesort (key1,key2,key3,key4)values ('003','a08','b08','c08');

注意这里共8条数据,key1=001的有4条,占50%(后面有用)

三、优化为using index

  MySQL的每条查询,都会先选择一个适合的索引。每个索引都有自己的字段列表,如上表索引名称为idx_key123,该索引字段列表为key1,key2,key3

1. 查询字段在索引字段列表中
1.1 没有where条件

只需要让order by后面的字段,遵循最左前缀即可。

mysql> explain select key1,key2 from test_filesort order by key1, key2;
+----+-------------+---------------+------------+-------+---------------+------------+---------+------+------+----------+-------------+
| id | select_type | table         | partitions | type  | possible_keys | key        | key_len | ref  | rows | filtered | Extra       |
+----+-------------+---------------+------------+-------+---------------+------------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | test_filesort | NULL       | index | NULL          | idx_key123 | 3069    | NULL |    8 |   100.00 | Using index |
+----+-------------+---------------+------------+-------+---------------+------------+---------+------+------+----------+-------------+

以下场景都会出现using filesort

  1. order by的字段顺序与索引字段顺序不一致
  2. order by的字段没有遵循最左前缀原则
  3. order by字段非连续
  4. order by字段升序降序混合

如:字段非连续

mysql> explain select key1,key2 from test_filesort order by key1, key3;
+----+-------------+---------------+------------+-------+---------------+------------+---------+------+------+----------+-----------------------------+
| id | select_type | table         | partitions | type  | possible_keys | key        | key_len | ref  | rows | filtered | Extra                       |
+----+-------------+---------------+------------+-------+---------------+------------+---------+------+------+----------+-----------------------------+
|  1 | SIMPLE      | test_filesort | NULL       | index | NULL          | idx_key123 | 3069    | NULL |    8 |   100.00 | Using index; Using filesort |
+----+-------------+---------------+------------+-------+---------------+------------+---------+------+------+----------+-----------------------------+
1.2 有where条件

只需要让where子句常量的字段 + order by后面的字段,遵循最左前缀即可;

mysql> explain select key1,key2 from test_filesort where key1='001' and key2>'a03' order by key2;
+----+-------------+---------------+------------+-------+---------------+------------+---------+------+------+----------+--------------------------+
| id | select_type | table         | partitions | type  | possible_keys | key        | key_len | ref  | rows | filtered | Extra                    |
+----+-------------+---------------+------------+-------+---------------+------------+---------+------+------+----------+--------------------------+
|  1 | SIMPLE      | test_filesort | NULL       | range | idx_key123    | idx_key123 | 2046    | NULL |    1 |   100.00 | Using where; Using index |
+----+-------------+---------------+------------+-------+---------------+------------+---------+------+------+----------+--------------------------+

如上:order by后是key2,只需要key1在where子句中是常量即可。

ps:WHERE子句中必须包含索引中最左的字段!但不一定是常量,因为最左前缀可以只出现在order by子句中,where只是起到一个选择索引的作用。

mysql> explain select * from test_filesort where key1>'002' order by key1,key2,key3;
+----+-------------+---------------+------------+-------+---------------+------------+---------+------+------+----------+-----------------------+
| id | select_type | table         | partitions | type  | possible_keys | key        | key_len | ref  | rows | filtered | Extra                 |
+----+-------------+---------------+------------+-------+---------------+------------+---------+------+------+----------+-----------------------+
|  1 | SIMPLE      | test_filesort | NULL       | range | idx_key123    | idx_key123 | 1023    | NULL |    1 |   100.00 | Using index condition |
+----+-------------+---------------+------------+-------+---------------+------------+---------+------+------+----------+-----------------------+

举个反例:

mysql> explain select key1,key2 from test_filesort where key1='001' and key2>'a03' order by key3;
+----+-------------+---------------+------------+-------+---------------+------------+---------+------+------+----------+------------------------------------------+
| id | select_type | table         | partitions | type  | possible_keys | key        | key_len | ref  | rows | filtered | Extra                                    |
+----+-------------+---------------+------------+-------+---------------+------------+---------+------+------+----------+------------------------------------------+
|  1 | SIMPLE      | test_filesort | NULL       | range | idx_key123    | idx_key123 | 2046    | NULL |    1 |   100.00 | Using where; Using index; Using filesort |
+----+-------------+---------------+------------+-------+---------------+------------+---------+------+------+----------+------------------------------------------+
2. 查询字段不在索引字段列表中

只需判断查出的结果行,是否小于总行数的50%即可。如果小于50%就不会出现using filesort的情况。

select后面尽量都是索引列表中的字段,避免回表。

mysql> explain select * from test_filesort where key1='001' order by key2,key3;
+----+-------------+---------------+------------+------+---------------+------+---------+------+------+----------+-----------------------------+
| id | select_type | table         | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra                       |
+----+-------------+---------------+------------+------+---------------+------+---------+------+------+----------+-----------------------------+
|  1 | SIMPLE      | test_filesort | NULL       | ALL  | idx_key123    | NULL | NULL    | NULL |    8 |    50.00 | Using where; Using filesort |
+----+-------------+---------------+------------+------+---------------+------+---------+------+------+----------+-----------------------------+
1 row in set (0.00 sec)
 
mysql> explain select * from test_filesort where key1='002' order by key2,key3;
+----+-------------+---------------+------------+------+---------------+------------+---------+-------+------+----------+-----------------------+
| id | select_type | table         | partitions | type | possible_keys | key        | key_len | ref   | rows | filtered | Extra                 |
+----+-------------+---------------+------------+------+---------------+------------+---------+-------+------+----------+-----------------------+
|  1 | SIMPLE      | test_filesort | NULL       | ref  | idx_key123    | idx_key123 | 1023    | const |    3 |   100.00 | Using index condition |
+----+-------------+---------------+------------+------+---------------+------------+---------+-------+------+----------+-----------------------+

如上key1='001’符合条件的有4条,正好50%,就使用了using filesort;而key='002’符合条件的有3条数据,占比37.5%,没有使用using filesort。


参考:https://dev.mysql.com/doc/refman/5.7/en/order-by-optimization.html#order-by-filesort

你可能感兴趣的:(MySQL,using,filesort,Extra,执行计划,排序优化)