Using filesort
是MySQL执行计划Extra字段
中的一个重要值,表示查询出所有数据再进行排序。此排序方式为文件排序,没有走索引排序using index
.
一般来说,执行计划中如果Extra字段
中值为Using filesort
时,那么type字段
(查询类型)一般为index
或ALL
。(两者都是查询所有数据,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
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
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%
(后面有用)
MySQL的每条查询,都会先选择一个适合的索引。每个索引都有自己的字段列表,如上表索引名称为idx_key123
,该索引字段列表为key1
,key2
,key3
只需要让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
如:字段非连续
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 |
+----+-------------+---------------+------------+-------+---------------+------------+---------+------+------+----------+-----------------------------+
只需要让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 |
+----+-------------+---------------+------------+-------+---------------+------------+---------+------+------+----------+------------------------------------------+
只需判断查出的结果行,是否小于总行数的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