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;
     

                    
       


     
    

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章