背景知识
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
[
](https://blog.csdn.net/Bronze5/article/details/113817719)
表准备
CREATE TABLE `using_index`
(
`id` int NOT NULL auto_increment,
`a` varchar(255) DEFAULT NULL,
`b` varchar(255) DEFAULT NULL,
`c` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_abc` (`a`, `b`, `c`)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci;
插入数据:
insert into using_index(a, b, c) value ('a01', 'b01', 'c01');
insert into using_index(a, b, c) value ('a02', 'b02', 'c02');
insert into using_index(a, b, c) value ('a03', 'b03', 'c03');
insert into using_index(a, b, c) value ('a04', 'b04', 'c04');
insert into using_index(a, b, c) value ('a00', 'b04', 'c04');
insert into using_index(a, b, c) value ('a00', 'b04', 'c04');
insert into using_index(a, b, c) value ('a00', 'b04', 'c04');
insert into using_index(a, b, c) value ('a00', 'b04', 'c04');
insert into using_index(a, b, c) value ('a00', 'b00', 'c04');
MySQL的每条查询,都会先选择一个适合的索引。每个索引都有自己的字段列表,如表索引名称为idx_abc,该索引包含三个字段:a,b,c
场景
查询字段在索引列表中
1.1 没有where条件只有order by
只需要让order by后面的字段,遵循最左前缀即可
mysql> explain select a,b,c from using_index order by a,b;
+----+-------------+-------------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| 1 | SIMPLE | using_index | NULL | index | NULL | idx_abc | 3069 | NULL | 9 | 100.00 | Using index |
+----+-------------+-------------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.01 sec)
上述SQL走:idx_abc索引,基于索引a_b_c的有序数据返回
以下场景都会出现using filesort
- order by的字段顺序与索引字段顺序不一致
- order by的字段没有遵循最左前缀原则
- order by字段非连续
- order by字段升序降序混合
如排序字段不连续, 无法按最左前缀匹配:
mysql> explain select a,b,c from using_index order by a,c;
+----+-------------+-------------+------------+-------+---------------+---------+---------+------+------+----------+-----------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------------+------------+-------+---------------+---------+---------+------+------+----------+-----------------------------+
| 1 | SIMPLE | using_index | NULL | index | NULL | idx_abc | 3069 | NULL | 9 | 100.00 | Using index; Using filesort |
+----+-------------+-------------+------------+-------+---------------+---------+---------+------+------+----------+-----------------------------+
1 row in set, 1 warning (0.00 sec)
1.2 有where + 常量字段+ order by
where子句常量的字段 + order by后面的字段,遵循最左前缀即可
mysql> explain select a,b,c from using_index where a='a01' order by b;
+----+-------------+-------------+------------+------+---------------+---------+---------+-------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------------+------------+------+---------------+---------+---------+-------+------+----------+-------------+
| 1 | SIMPLE | using_index | NULL | ref | idx_abc | idx_abc | 1023 | const | 1 | 100.00 | Using index |
+----+-------------+-------------+------------+------+---------------+---------+---------+-------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
上述SQL走:idx_abc索引,命中字段a,然后基于b排序,本质还是最左前缀匹配,通过a字段来选择索引。
1.3 where + 范围查询字段 + order by
通过a字段查找索引后,然后走索引内部有序,避免filesort
mysql> explain select a,b,c from using_index where a >='a01' order by a,b;
+----+-------------+-------------+------------+-------+---------------+---------+---------+------+------+----------+--------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------------+------------+-------+---------------+---------+---------+------+------+----------+--------------------------+
| 1 | SIMPLE | using_index | NULL | range | idx_abc | idx_abc | 1023 | NULL | 4 | 100.00 | Using where; Using index |
+----+-------------+-------------+------------+-------+---------------+---------+---------+------+------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)
查询字段不在索引列表中
只需判断查出的结果行,是否小于总行数的50%即可。如果小于50%就不会出现using filesort的情况。
注:select后面尽量都是索引列表中的字段,避免回表。
mysql> explain select id,a,b,c from using_index where a='a00' order by b,c;
+----+-------------+-------------+------------+------+---------------+---------+---------+-------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------------+------------+------+---------------+---------+---------+-------+------+----------+-------------+
| 1 | SIMPLE | using_index | NULL | ref | idx_abc | idx_abc | 1023 | const | 8 | 100.00 | Using index |
+----+-------------+-------------+------------+------+---------------+---------+---------+-------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
最新版本的MySQL测试显示:a=a00,结果行大于总数的50%,也会走索引匹配。