MySQL之索引优化(二)
小表驱动大表
ORDER BY 优化
模拟数据
create table tblA(
#id int primary key not null auto_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);
查看执行计划
案例A
EXPLAIN SELECT * FROM tblA WHERE age > 20 ORDER BY age
EXPLAIN SELECT * FROM tblA WHERE age > 20 ORDER BY age,birth
EXPLAIN SELECT * FROM tblA WHERE age > 20 ORDER BY birth
EXPLAIN SELECT * FROM tblA WHERE age > 20 ORDER BY birth,age
EXPLAIN SELECT * FROM tblA WHERE birth > '2021-02-19 22:45:00' ORDER BY birth
EXPLAIN SELECT * FROM tblA WHERE birth > '2021-02-19 22:45:00' ORDER BY age
案例B
EXPLAIN SELECT * FROM tblA ORDER BY age ASC,birth DESC
结论
- MySQL支持两种方式的排序,index和filesort。index效率高,它是指扫描索引本身完成排序,filesort效率低。
- ORDER BY子句,尽量使用Index方式排序,避免filesort方式排序。
- ORDER BY子句满足两种情况,会使用index排序,一是ORDER BY子句采用遵照最佳左前缀法则,二是where条件字段和ORDER BY子句组合起来,满足最佳左前缀法则
排序分组优化
MySQL的排序算法
当发生 Using filesort 时,MySQL会根据自己的算法对查询结果进行排序
双路排序
- MySQL 4.1 之前是使用双路排序,字面意思就是两次扫描磁盘,最终得到数据,读取行指针和 order by 列,对他们进行排序,然后扫描已经排序好的列表,按照列表中的值重新从列表中读取对应的数据输出.
- 从磁盘取排序字段,在 buffer 进行排序,再从磁盘取其他字段
简单来说,取一批数据,要对磁盘进行了两次扫描,众所周知,I\O 是很耗时的,所以在 mysql4.1 之后,出现了第二种改进的算法,就是单路排序
单路排序
- 从磁盘读取查询需要的所有列,按照 order by 列在 buffer 对它们进行排序,然后扫描排序后的列表进行输出, 它的效率更快一些,避免了第二次读取数据。并且把随机 IO 变成了顺序 IO,但是它会使用更多的空间, 因为它把每一行都保存在内存中了
存在的问题
- 在 sort_buffer 中,方法 B 比方法 A 要多占用很多空间,因为方法 B 是把所有字段都取出, 所以有可能取出的数据的总大小超出了 sort_buffer 的容量,导致每次只能取 sort_buffer 容量大小的数据,进行排序(创建 tmp 文件,多 路合并),排完再取取 sort_buffer 容量大小,再排……从而多次 I/O。也就是本来想省一次 I/O 操作,反而导致了大量的 I/O 操作,反而得不偿失
如何优化
-
增大
sort_buffer_size
参数的设置不管用哪种算法,提高这个参数都会提高效率,当然,要根据系统的能力去提高,因为这个参数是针对每个进程的 1M-8M 之间调整
-
增大
max_length_for_sort_data
参数的设置mysql 使用单路排序的前提是排序的字段大小要小于 max_length_for_sort_data, 提高这个参数,会增加使用改进算法的概率。
但是如果设的太高,数据总容量超出 sort_buffer_size 的概率反而会增大, 就会出现高频磁盘 I/O 和低的处理器使用率。(1024-8192 之间调整)
总结 -
减少 select 后面的查询的字段(少用select *)
查询的字段减少,缓冲就能容纳更多的内容,也就相当于间接增大了sort_buffer_size
总结A
总结B
GROUP BY 优化
GROUP BY 优化和ORDER BY大致类似
- 要想在排序时使用索引,避免 Using filesort,可以采用索引覆盖
- ORDER BY /GROUP BY后面字段的顺序要和复合索引的顺序完全一致
- ORDER BY /GROUP BY后面的索引必须按照顺序出现,排在后面的可以不出现
- 要进行升序或者降序时,字段的排序顺序必须一致。不能一部分升序,一部分降序,可以都升序或者都降序
- 如果复合索引前面的字段作为常量出现在过滤条件中,排序字段可以为紧跟其后的字段