学习《MySQL 高级》高阳老师解说索引课程的笔记,本篇偏重对 order by 排序剖析
建表
# 建表
CREATE TABLE tblA(
#id int primary key not null autp_increment,
age int,
birth timestamp not null
);
insert into tblA(age,birth) values(22,now());
insert into tblA(age,birth) values(23,now());
insert into tblA(age,birth) values(24,now());
# 建设复合索引
CREATE INDEX idx_A_ageBirth on tblA(age,birth);
select * from tblA;
Order By 优化(索引剖析)
因为本表中只有两个字段 age, birth,复合索引都笼罩了,所以 select * 就相当于 select age, birth, 查问间接走索引,不须要回表。此处仅关注排序(order by)是否会呈现文件排序(filesort)。
1.1 explain select * from tblA where age > 20 order by age;
+----+-------------+-------+------------+-------+----------------+----------------+---------+------+------+----------+--------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+----------------+----------------+---------+------+------+----------+--------------------------+
| 1 | SIMPLE | tblA | NULL | index | idx_A_ageBirth | idx_A_ageBirth | 9 | NULL | 3 | 100.00 | Using where; Using index |
+----+-------------+-------+------------+-------+----------------+----------------+---------+------+------+----------+--------------------------+
排序用到了 age 字段的索引,不会呈现 filesort。
1.2 explain select * from tblA where age > 20 order by age, birth;
+----+-------------+-------+------------+-------+----------------+----------------+---------+------+------+----------+--------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+----------------+----------------+---------+------+------+----------+--------------------------+
| 1 | SIMPLE | tblA | NULL | index | idx_A_ageBirth | idx_A_ageBirth | 9 | NULL | 3 | 100.00 | Using where; Using index |
+----+-------------+-------+------------+-------+----------------+----------------+---------+------+------+----------+--------------------------+
排序,age, birth 合乎复合索引程序,所以排序用到了 age, birth 两个字段的索引,不会呈现 filesort。
*1.3 explain select * from tblA where age > 20 order by birth;
+----+-------------+-------+------------+-------+----------------+----------------+---------+------+------+----------+------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+----------------+----------------+---------+------+------+----------+------------------------------------------+
| 1 | SIMPLE | tblA | NULL | index | idx_A_ageBirth | idx_A_ageBirth | 9 | NULL | 3 | 100.00 | Using where; Using index; Using filesort |
+----+-------------+-------+------------+-------+----------------+----------------+---------+------+------+----------+------------------------------------------+
查问时用到了 age 字段的索引;
排序时,因为 age 是范畴,范畴前面全生效,所以不能走 age, birth 索引进行排序,那么就会呈现 filesort。
若 age 是个等值查问,排序时就不会呈现 filesort。见上面语句:
mysql> explain select * from tblA where age = 22 order by birth;
+----+-------------+-------+------------+------+----------------+----------------+---------+-------+------+----------+--------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+----------------+----------------+---------+-------+------+----------+--------------------------+
| 1 | SIMPLE | tblA | NULL | ref | idx_A_ageBirth | idx_A_ageBirth | 5 | const | 1 | 100.00 | Using where; Using index |
+----+-------------+-------+------------+------+----------------+----------------+---------+-------+------+----------+--------------------------+
1.4 explain select * from tblA where age > 20 order by birth, age;
+----+-------------+-------+------------+-------+----------------+----------------+---------+------+------+----------+------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+----------------+----------------+---------+------+------+----------+------------------------------------------+
| 1 | SIMPLE | tblA | NULL | index | idx_A_ageBirth | idx_A_ageBirth | 9 | NULL | 3 | 100.00 | Using where; Using index; Using filesort |
+----+-------------+-------+------------+-------+----------------+----------------+---------+------+------+----------+------------------------------------------+
排序时,因为 birth, age 不合乎复合索引的程序,所以会呈现 filesort。
2.1 explain select * from tblA order by birth;
+----+-------------+-------+------------+-------+---------------+----------------+---------+------+------+----------+-----------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+----------------+---------+------+------+----------+-----------------------------+
| 1 | SIMPLE | tblA | NULL | index | NULL | idx_A_ageBirth | 9 | NULL | 3 | 100.00 | Using index; Using filesort |
+----+-------------+-------+------------+-------+---------------+----------------+---------+------+------+----------+-----------------------------+
排序时,仅有 birth,会呈现 filesort。
2.2 explain select * from tblA where birth > ‘2016-01-28 00:00:00’ order by birth;
+----+-------------+-------+------------+-------+---------------+----------------+---------+------+------+----------+------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+----------------+---------+------+------+----------+------------------------------------------+
| 1 | SIMPLE | tblA | NULL | index | NULL | idx_A_ageBirth | 9 | NULL | 3 | 33.33 | Using where; Using index; Using filesort |
+----+-------------+-------+------------+-------+---------------+----------------+---------+------+------+----------+------------------------------------------+
排序时,where 和 order by 的字段程序不合乎复合索引程序,会呈现文件排序。
*2.3 explain select * from tblA where birth > ‘2016-01-28 00:00:00’ order by age;
+----+-------------+-------+------------+-------+---------------+----------------+---------+------+------+----------+--------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+----------------+---------+------+------+----------+--------------------------+
| 1 | SIMPLE | tblA | NULL | index | NULL | idx_A_ageBirth | 9 | NULL | 3 | 33.33 | Using where; Using index |
+----+-------------+-------+------------+-------+---------------+----------------+---------+------+------+----------+--------------------------+
排序时,where 和 order by 的字段程序通过优化器优化后合乎合乎索引的程序,不会呈现文件排序。
*2.4 explain select * from tblA order by age asc, birth desc;
+----+-------------+-------+------------+-------+---------------+----------------+---------+------+------+----------+-----------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+----------------+---------+------+------+----------+-----------------------------+
| 1 | SIMPLE | tblA | NULL | index | NULL | idx_A_ageBirth | 9 | NULL | 3 | 100.00 | Using index; Using filesort |
+----+-------------+-------+------------+-------+---------------+----------------+---------+------+------+----------+-----------------------------+
排序时,order by 的字段 age、birth 尽管合乎合乎索引程序,然而 age 按升序、birth 按降序,这样无奈利用索引排序,会呈现文件排序。若 age、birth 同升或同降,则不会呈现文件排序。
Order By 无奈应用索引时的优化
如果 Order By 用不上索引的话,就会呈现 filesort。filesort 有两种算法:双路排序
和单路排序
。
双路排序
MySQL4.1 之前是应用双路排序,字面意思就是两次扫描磁盘,最终失去数据,
读取行指针和 orderby 列,对他们进行排序,而后扫描曾经排序好的列表,依照列表中的值从新从列表中读取对应的数据输入。从磁盘取排序字段,在 buffer 进行排序,再从磁盘取其余字段。
取一批数据,要对磁盘进行了两次扫描,家喻户晓,I/ O 是很耗时的,所以在 mysql4.1 之后,呈现了第二种改良的算法,就是单路排序。
单路排序
从磁盘读取查问须要的所有列,依照 order by 列在 buffer 对它们进行排序,而后扫描排序后的列表进行输入,它的效率更快些,防止了第二次读取数据。并且把随机 IO 变成了程序 IO,然而它会应用更多的空间,因为它把每一行都保留在内存中了。
一般来说 单路排序
更好,但也会呈现问题。这是为什么?
在 sort_buffer 中,单路排序
比多路排序
要多占用很多空间,因为 单路排序
是把所有字段都取出,所以有可能取出的数据的总大小超出了 sort_buffer
的容量,导致每次只能取 sort_ buffer 容量大小的数据,进行排序(创立 tmp 文件,多路合并),排完再取 sort_buffer 容量大小,再排 … 从而屡次 I /O。
原本想省一次 I / O 操作,反而导致了大量的 I / O 操作,反而得失相当。
如何优化?通过减少 sort_buffer_size
容量和max_length_for_sort_data
进步 Order By 的速度
1.Order by 时 select * 是一个大忌,只查须要的字段,这点十分重要。在这里的影响是:
1.1 当 Query 的字段大小总和小于 max_length_for_sort_data 而且排序字段不是 TEXT|BLOB 类型时,会用改良后的算法——单路排序,否则用老算法——多路排序。
1.2 两种算法的数据都有可能超出 sort_buffer 的容量,超出之后,会创立 tmp 文件进行合并排序,导致屡次 I /O,然而用单路排序算法的危险会更大一些,所以要进步 sort_buffer_size。
2. 尝试进步 sort_buffer_size
不论用哪种算法,进步这个参数都会提高效率,当然,要依据零碎的能力去进步,因为这个参数是针对每个过程的。
3. 尝试进步 max_length_for_sort_data
进步这个参数,会减少用改良算法的概率。然而如果设的太高,数据总容量超出 sort_buffer_size 的概率就增大,显著症状是高的磁盘 I / O 流动和低的处理器使用率。
Group By 优化
1.Gourp By 实际上是先排序再分组,恪守复合索引的最佳左前缀准则;
2. 当无奈应用索引列时,也须要像 Order By 一样,减少 max_length_for_sort_data 和 sort_buffer_size;
3.where 先于 having,尽量用 where 进行条件过滤。
SQL 调优程序
- 开启慢查问捕捉慢日志
- explain + 剖析慢 SQL
- show profile 查问 SQL 在 MySQL 服务器外面的执行细节和生命周期状况
- MySQL 服务器参数调优(DBA)
[参考]
B 站《MySQL 高级》48.MySQL 高级_为排序应用索引 OrderBy 优化
MySQL 高级篇(高阳)建表 sql 语句大全