order by:
create table test04(
id int primary key auto_increment,
c1 char(10),
c2 char(10),
c3 char(10),
c4 char(10),
c5 char(10)
);
insert into test04(c1,c2,c3,c4,c5)
values
('a1','a2','a3','a4','a5'),
('b1','b2','b3','b4','b5'),
('c1','c2','c3','c4','c5'),
('d1','d2','d3','d4','d5'),
('e1','e2','e3','e4','e5');
建索引:create index inx_1234 on test04(c1,c2,c3,c4);
mysql> explain select * from test04 where c1 = 'a1';
+----+-------------+--------+------------+------+---------------+----------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+------+---------------+----------+---------+-------+------+----------+-------+
| 1 | SIMPLE | test04 | NULL | ref | inx_1234 | inx_1234 | 31 | const | 1 | 100.00 | NULL |
+----+-------------+--------+------------+------+---------------+----------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.13 sec)
mysql> explain select * from test04 where c2 = 'a2' and c1 = 'a1' and c4 = 'c4' and c3 = 'c3';
+----+-------------+--------+------------+------+---------------+----------+---------+-------------------------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+------+---------------+----------+---------+-------------------------+------+----------+-------+
| 1 | SIMPLE | test04 | NULL | ref | inx_1234 | inx_1234 | 124 | const,const,const,const | 1 | 100.00 | NULL |
+----+-------------+--------+------------+------+---------------+----------+---------+-------------------------+------+----------+-------+
1 row in set, 1 warning (0.06 sec)
mysql> explain select * from test04 where c2 = 'a2' and c4 = 'c4' and c3 = 'c3';
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | test04 | NULL | ALL | NULL | NULL | NULL | NULL | 5 | 20.00 | Using where |
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
mysql> explain select * from test04 where c1 = 'a1' and c4 = 'c4' and c3 = 'c3';
+----+-------------+--------+------------+------+---------------+----------+---------+-------+------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+------+---------------+----------+---------+-------+------+----------+-----------------------+
| 1 | SIMPLE | test04 | NULL | ref | inx_1234 | inx_1234 | 31 | const | 1 | 20.00 | Using index condition |
+----+-------------+--------+------------+------+---------------+----------+---------+-------+------+----------+-----------------------+
1 row in set, 1 warning (0.00 sec)
说明即便是查询的顺序和索引顺序不一致,索引也不会失效。但是一旦使用索引时有跨列的行为,被跨列的后面的复合索引都会失效。
当有范围查询时:
mysql> explain select * from test04 where c1 = 'a1' and c2 = 'a2' and c3 > 'a3' and c4 = 'c4';
+----+-------------+--------+------------+-------+---------------+----------+---------+------+------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+-------+---------------+----------+---------+------+------+----------+-----------------------+
| 1 | SIMPLE | test04 | NULL | range | inx_1234 | inx_1234 | 93 | NULL | 1 | 20.00 | Using index condition |
+----+-------------+--------+------------+-------+---------------+----------+---------+------+------+----------+-----------------------+
1 row in set, 1 warning (0.00 sec)
从key看出确实用到了索引,虽然ref是NULL。但是符合范围之后全失效的原则。
mysql> explain select * from test04 where c1 = 'a1' and c2 = 'a2' and c4 > 'a4' and c3 = 'c3';
+----+-------------+--------+------------+-------+---------------+----------+---------+------+------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+-------+---------------+----------+---------+------+------+----------+-----------------------+
| 1 | SIMPLE | test04 | NULL | range | inx_1234 | inx_1234 | 124 | NULL | 1 | 100.00 | Using index condition |
+----+-------------+--------+------------+-------+---------------+----------+---------+------+------+----------+-----------------------+
1 row in set, 1 warning (0.06 sec)
结合上面的例子可以看出用到了4个索引,从key_len可以看出。
mysql> explain select * from test04 where c1 = 'a1' and c2 = 'a2' and c4 > 'a4' order by c3 ;
+----+-------------+--------+------------+------+---------------+----------+---------+-------------+------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+------+---------------+----------+---------+-------------+------+----------+-----------------------+
| 1 | SIMPLE | test04 | NULL | ref | inx_1234 | inx_1234 | 62 | const,const | 1 | 33.33 | Using index condition |
+----+-------------+--------+------------+------+---------------+----------+---------+-------------+------+----------+-----------------------+
1 row in set, 1 warning (0.05 sec)
用到了两个索引,从排序的角度理解,c3的也用到了。
mysql> explain select * from test04 where c1 = 'a1' and c2 = 'a2' order by c4 ;
+----+-------------+--------+------------+------+---------------+----------+---------+-------------+------+----------+----------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+------+---------------+----------+---------+-------------+------+----------+----------------+
| 1 | SIMPLE | test04 | NULL | ref | inx_1234 | inx_1234 | 62 | const,const | 1 | 100.00 | Using filesort |
+----+-------------+--------+------------+------+---------------+----------+---------+-------------+------+----------+----------------+
1 row in set, 1 warning (0.00 sec)
一定只用到了两个索引。并且出现了using filesort.
mysql> explain select * from test04 where c1 = 'a1' and c5 = 'a5' order by 'c2,c3';
+----+-------------+--------+------------+------+---------------+----------+---------+-------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+------+---------------+----------+---------+-------+------+----------+-------------+
| 1 | SIMPLE | test04 | NULL | ref | inx_1234 | inx_1234 | 31 | const | 1 | 20.00 | Using where |
+----+-------------+--------+------------+------+---------------+----------+---------+-------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
用到了一个查找索引,两个排序索引。c5是干扰项。
mysql 8> explain select * from test04 where c1 = 'a1' and c5 = 'a5' order by 'c3,c2';
+----+-------------+--------+------------+------+---------------+----------+---------+-------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+------+---------------+----------+---------+-------+------+----------+-------------+
| 1 | SIMPLE | test04 | NULL | ref | inx_1234 | inx_1234 | 31 | const | 1 | 20.00 | Using where |
+----+-------------+--------+------------+------+---------------+----------+---------+-------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
以上两个例子,第二个在低版本中会出现using filesort.
一般情况下如果order by的顺序和复合索引的顺序不一致都会出现using filesort,前提是查找列中无排序列。
order by 子句尽量使用index方式排序,避免使用filesort方式排序,且尽可能在索引上完成排序稻作,遵照索引建的最佳左前缀法则。
create table tabA(
age int,
birth timestamp not null
);
insert into tabA(age,birth) values (23,now());
insert into tabA(age,birth) values (24,now());
insert into tabA(age,birth) values (25,now());
insert into tabA(age,birth) values (26,now());
create index idx_a_b on tabA(age,birth);
mysql> explain select * from tabA where age > 20 order by age;
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+--------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+--------------------------+
| 1 | SIMPLE | tabA | NULL | index | idx_a_b | idx_a_b | 9 | NULL | 4 | 100.00 | Using where; Using index |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)
mysql> explain select * from tabA where age > 20 order by birth;
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+------------------------------------------+
| 1 | SIMPLE | tabA | NULL | index | idx_a_b | idx_a_b | 9 | NULL | 4 | 100.00 | Using where; Using index; Using filesort |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+------------------------------------------+
1 row in set, 1 warning (0.00 sec)
从以上两个列子可以看出虽然用到了索引,但是如果order by 的顺序和索引建的顺序不一致的时候会产生using filesort.
mysql> explain select * from tabA order by age asc,birth desc;
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-----------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-----------------------------+
| 1 | SIMPLE | tabA | NULL | index | NULL | idx_a_b | 9 | NULL | 4 | 100.00 | Using index; Using filesort |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-----------------------------+
1 row in set, 1 warning (0.01 sec)
因为order by默认升序两个排序存在冲突,因此会产生using filesort.
此时仅仅依靠索引是无法解决的,需要修改mysql的配置文件的
sort_buffer_size的容量,同时可能也需要改max_length_for_size_data的容量。
group by的优化原则和order by差不多:
(1)分组顺序按照复合索引的最佳左前缀原则进行;
(2)当分组时无法直接使用索引时应考虑修改配置文件的sort_buffer_size,max_length_for_size_data
具体修改的大小依照具体情况而定;
(3)having语句基于group by,但是也是用于过滤,因此能在where后直接过滤的尽量不用在使用having;