mysql----order by优化

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;
     

                    
       


     
    

发布了76 篇原创文章 · 获赞 21 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/shen_chengfeng/article/details/104598701
今日推荐